Is there anything more beneficial to using Timestamp vs DateTime?

Is there anything more beneficial to using Timestamp vs DateTime?

Old forum URL: forums.lhotka.net/forums/t/3805.aspx


Pawz posted on Monday, October 29, 2007

I'm just starting to get stuck into CSLA, after working for several years with various frameworks built from the ground up by this or that company - I must say the idea of having a good basis to start building business applications was enough to get me hooked.

In any case, I've been going through the book, and trying to build a small application alongside the chapters on building the Project Tracker. I've been trying to figure out some of the 'best practices' one could use, especially when designing a system from the BO -> data rather than the other way around. I have the opportunity at the moment to build the database entirely from scratch, so a couple things kind of jumped out at me.

One thing I've run into is the Timestamp coloumn - historically I've always used a datetime field with a userid field to track when and who has made a change to the database. I'm tempted to go the same route, but not being familiar with the framework, I'm not so sure if I should dump timestamp in favor of datetime.
Is there any additional benefit to using the timestamp field other than that it handles updating itself automatically?

The second point was the use of the Guid field - in the book, it talks about it very briefly and highlights how it allows the BO to control the generation of its own ID... is it a good idea to make *all* tables use a Guid primary key as the record ID?

tetranz replied on Monday, October 29, 2007

Timestamps are somewhat badly named and don't have much to do with date or time. I think the newer, more correct, name for a timestamp is "row version". It's a field that changes automatically on every update so in some ways it's similar to an auto incrementing identity column except that it's an (I think) 8 byte binary. Its purpose is so that you can easily check if someone else has updated the row while you were editing it. The usual way is in the WHERE clause.  i.e, WHERE id = @OldId and timestamp = @OldTimestamp . Then check to see if you actually updated anything.

You could (sort of) achieve the same with a datetime field but its not as reliable. Someone else (not using your application) could update the row but not update the datetime field. There is nothing to stop you including a timestamp and a datetime but they're for different purposes.

The whole guid versus integer id thing is an old discussion point that has been beaten to death in the past and has no right or wrong answer. Guids have a small performance penalty (how much and is it significant depends on lots of things) and use more disk space but they sure are convenient.

Cheers
Ross

Pawz replied on Tuesday, October 30, 2007

Hmm perhaps I'll just rename it to 'RowVersion' in the db to keep it clear. I was looking at the two fields ("LastModified" - a date / time stamp to show users when it was last modified, and "LastChanged" - the timestamp) and it wasn't making a whole lot of sense to have both, but I can see where it would be valuable to have a sure-fire way to know that the row hasn't changed.

As for Guid, looks like I'll go with it for my new db. If it's easier to use at the cost of some slight performance, I'm quite happy to go with ease of use (it's not going to be a huge system in any case).


Another question I've had while working through this is about the Authorization Rules. In the interests of not spamming the forum ;) I'll post it here.

I'm guessing it works differently in other companies, but in this one, the system is under constant change - certain people are given access to different areas on a shifting basis, based on staff and responsibility changes that are constantly getting updated. As such, the idea of hard coding any kind of authorization rules in is setting off red flags in my head.

I'm working out a solution that builds the authorization roles from the db, but obviously I don't particularly want to build those rules when the static methods are called (CanAddObject) and so on. My tentative current solution is to add for each BO the 4 roles to the security roles table (CanAddAssemblyTeam, CanGetAssemblyTeam, CanEditAssemblyTeam, CanDeleteAssemblyTeam), and then hardcode those roles into the BO.

Is this the right way to be going about it? Are there better methods?

DavidDilworth replied on Tuesday, October 30, 2007

On the Timestamp vs DateTime issue I think your suggestion sounds fine.  We use NHibernate and that provides automatic support for optimistic concurrency checking using a "Version" column on each table in the database.  So our convention is to use an integer column called "Version" - it's the version number of the Business Object.  We have separate DateTime columns for audit trail style functionality.

On the Authorization issue it sounds again like you're thinking the right way.  Make sure you think of the "Roles" that users will perform in your application.   Don't think of it as the users called "Luke" and "Darth Vader".  Think of it as users who get mapped to the Role "GoodGuys" and users who get mapped to the Role "BadGuys".  That may help when you think about what Role Based Security is all about.

KKoteles replied on Tuesday, October 30, 2007

Pawz,

With respect to your question regarding Authorization Rules...  At some point, in order to make decisions within your business object as to who can do what - you are going to have to "hard code" something. 

I had a situation where the business wanted to create their own roles, "define" those roles, and then assign users to those roles.  Kind of tricky to use role based permissions when you don't even know what the names of the roles might be.  Instead we ended up creating "permissions".  Those permissions were used to define the roles.  This way the users could create whatever roles they wanted, assigned whatever permissions they wanted to the roles, and then assign the users to the roles.  All we ever cared about was the permissions.  Obviously the users could NOT create permissions.  When the user logged in, the stored procedure did all the hard work gathering all the permissions the user had.  Then we just threw the permissions into the custom security context instead of the roles.  In the objects, when we used IsInRole() what we were actually checking was if they were assigned a particular permission or not.  As the requirements changed, we would change / update the permissions and change / update our business objects accordingly.

Ken

JoeFallon1 replied on Tuesday, October 30, 2007

I did something very similar to what Ken describes with Permissions. We have both Roles and Permissions though. Turns out we don't do much with the Roles  - so we might have been better off not including them. Then IsInRole could be used to check permissions as Ken describes. This is useful in many places in CSLA and .Net. I have a corresponding method named HasPermission but it is not part of the interface nor does Rocky call it or allow it ot be called (yet.) He is considering adding Delegate support to CSLA so IsInRole is not the only allowed method to be called.

All in all, I wish I did not have any roles and had gone with the idea of using permissions for the IsInRole method. But we did not know all this 4 years ago when we first started.

Joe

Pawz replied on Tuesday, October 30, 2007

How would you implement those 'permissions' on a BO? I'm pretty sure I'm thinking along the same lines as you guys with regards to allowing the object to be read/created in the static factory methods, but I'm not sure how best to generate the list of permissions for each property.

Would I get something to generate the code for 'roles' like "ProjectNameAllowRead", "ProjectNameAllowWrite" etc etc? It seems laborious to make the 4 permissions for each property but I suppose I could make a snippet to do most of the work.. alternatively I was thinking of storing a list of them in the db and loading that list up when the AddAuthorizationRules() function is called. That would be slower though...

Pawz replied on Tuesday, October 30, 2007

The other question is, can I make a direct call to the database from the AddAuthorizationRules function without breaking the whole DataPortal concept? Does AddAuthorizationRules get called on the server or the client, or more importantly, does it get called wherever the object is created...

KKoteles replied on Wednesday, October 31, 2007

Pawz,

You would create the permissions as you need them.  In some cases I had a permission called "ProjectName Application Owner" (PAO) and another permission called "ProjectName Permission Administrator" (PPA).  For the maintenance of all the lookup tables, only the users assigned either of these permissions (roles) could save the object; however, only a PPA could deactivate an object while both could update the descriptions.  Everything works the same as described in Rocky's books and the ProjectTracker example.

The big difference is in the stored procedure you call within the DataPortal_Fetch of your security Identity object.  Here is a watered down version of my Login stored procedure:

SELECT DISTINCT
      RTrim([Permissions].PermissionDisplayName) As Role
FROM Roles
      Left Join Sessions ON Roles.RoleID = Sessions.RoleID
      Left Join Users ON Sessions.UserID = Users.UserID
      Left Join Access ON Roles.RoleID = Access.RoleID
      Left Join [Permissions] ON Access.PermissionID = [Permissions].PermissionID
WHERE Users.UserName = @UserName
      AND Roles.ActiveFlag = 1
      AND Users.ActiveFlag = 1
      AND [Permissions].ActiveFlag = 1

Notice how I am returning my permission as a role (which then gets added to the List _roles member within your Identity object).  That is because I didn't want to change the way anything else within the objects worked - all I wanted to do is feed it the information I wanted it to have.  The Sessions table is a join table between Roles and Users.  This is where users get "assigned" to roles.  The Access table is a join table between Roles and Permissions.  This is where permissions get "assigned" to roles.  So basically I want every distinct permission a user has been "assigned" to where I match his name and everything associated with it is still "Active".

Here is how I setup the authorizations within the object:

        protected override void AddAuthorizationRules()
        {
            AuthorizationRules.AllowWrite("ActiveFlag", "ProjectName Permission Administrator");
           
AuthorizationRules.AllowWrite("Description", "ProjectName Permission Administrator", "ProjectName Application Owner");
        }

        public static bool CanEditObject()
        {
            bool result = false;

            if (Csla.ApplicationContext.User.IsInRole("ProjectName Permission Administrator"))
            {
                result = true;
            }
            else if (Csla.ApplicationContext.User.IsInRole("ProjectName Application Owner"))
            {
                result = true;
            }

            return result;
        }

If I need finer control (like on a field by field basis), I could add individual permissions for each field; however, I try to stay away from that unless I have no other option.  It becomes a maintenance nightmare for the person maintaining the roles (and their associated "assigned" permissions).  As far as your CSLA objects are concerned, your permissions are roles and will treat them as such.  Use them just the way you were going to use a role.  (It gets a little confusing, but it is really just a matter of semantics)

With respect to your question / concern regarding the sequencing of authorization rules, I really recommend purchasing and reading Rocky's CSLA .NET Version 2.1 Handbook.  I have the C# version and it covers your question in detail.  Take a look at "Changes to ValidationRules" on page 12, "Adding Per-Instance Validation Rules" on page 15 and "Adding Per-Type Validation Rules" on page 16.

Am I close to answering your question?

Ken

DavidDilworth replied on Wednesday, October 31, 2007

AddAuthorizationRules() is called from inside the constructor in BusinessBase and ReadOnlyBase.

So the answer to your question is "Yes" it would break the DataPortal concept if you tried to access the DB directly from inside this method override.

Create a new BO to get the data from the DB and then use this inside the AddAuthorizationRules()override.  That way you can be sure that the code you need to hit the DB will be executed on the App server.

Pawz replied on Wednesday, October 31, 2007

David - Good point, I hadn't even thought about using another BO to pull up the correct roles for whichever properties end up needing security.

Ken, that looks almost exactly like what I was thinking - good to know I'm on the right track. Pretty much just map permissions ('roles') to properties, although what I'm thinking is that the mapping will probably occur in the database and get loaded when the object is created.


Thanks for the help guys - it makes a big difference in the decision to use something like CSLA when I know I can get help!

DavidDilworth replied on Thursday, November 01, 2007

Pawz,

I want to issue an unreserved apology for my post yesterday.  I was wrong Sad [:(].

I was correct that the AddAuthorizationRules() method is called from inside the constructor in BusinessBase and ReadOnlyBase.

However, I was incorrect when I said that it would break the DataPortal concept to access the DB from within this method override.

All CSLA objects are instantiated by the server-side DataPortal.  Therefore, anything done as part of their construction will have access to the DB.

Needless to say I'm suitably embarrassed Embarrassed [:$]

Copyright (c) Marimer LLC