SubSonic & Oracle - Play Nice!

I've recently been working with SubSonic and Oracle.  One of the troubles I am dealing with is that by default, SubSonic requires your user to own the schema in order to generate classes for this.  This is because when it generates code it uses the USER_* objects to generate them.

This is a good practice but what happens when you don't own the schema?

What if...

Let's say you have been tasked out with allowing your application to query an HR database for user-specific information.  Your company, like most companies, is very restrictive on who has access to that HR data.  So, the DBA's create a view for you with just the information you need as well as an account to use when accessing it.

Now, this presents an issue because the account you have been given does not own the schema.  This puts you in a bind because you will find that when you try to generate objects with SubSonic, nothing will get generated.

Let's get this working right....

The quick fix to this is to open up and modify SubSonic. 
  1. Open the solution.
  2. In main SubSonic class, under the DataProviders folder, open the OracleDataProvider.cs file.
  3. Scroll down to about line 486
  4. Update the "Schema Bits" region to the following
#region Schema Bits

        private const string MANY_TO_MANY_LIST = "SELECT b.table_name FROM all_constraints a, all_cons_columns b " +
                                                  "WHERE a.table_name = :tableName " +
                                                  "AND a.constraint_type = 'R' " +
                                                  "AND a.r_constraint_name = b.constraint_name " +
                                                  "AND b.table_name like '%' + :mapSuffix";

        const string TABLE_COLUMN_SQL = "SELECT user, a.table_name, a.column_name, a.column_id, a.data_default, " +
                                        "       a.nullable, a.data_type, a.char_length, a.data_precision, a.data_scale " +
                                        "  FROM all_tab_columns a " +
                                        " WHERE a.table_name = :tableName";

        const string SP_PARAM_SQL = @"SELECT a.object_name, a.object_type, b.position, b.in_out,
                                    b.argument_name, b.data_type, b.char_length, b.data_precision, b.data_scale
                                    FROM all_objects a, all_arguments b
                                    WHERE a.object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE')
                                    AND a.object_id = b.object_id
                                    AND a.object_name = :objectName";

        const string SP_SQL = @"SELECT a.object_name, a.object_type, a.created, a.last_ddl_time
    `                           FROM all_objects a
                                WHERE a.object_type IN ('PROCEDURE', 'FUNCTION', 'PACKAGE') ";

        const string TABLE_SQL = "SELECT a.table_name AS Name FROM all_tables a";

        const string VIEW_SQL = "SELECT a.view_name AS Name FROM all_views a";

        const string INDEX_SQL =    "SELECT b.table_name, b.column_name, " +
                                    "       DECODE (a.constraint_type, " +
                                    "               'R', 'FOREIGN KEY', " +
                                    "               'P', 'PRIMARY KEY', " +
                                    "               'UNKNOWN' " +
                                    "              ) constraint_type " +
                                    "  FROM all_constraints a, all_cons_columns b " +
                                    " WHERE a.constraint_name = b.constraint_name " +
                                    "   AND a.constraint_type IN ('R', 'P') " +
                                    "   AND b.table_name = :tableName ";

        const string GET_TABLE_SQL =    "SELECT b.table_name " +
                                        "  FROM all_constraints a, all_cons_columns b " +
                                        " WHERE a.constraint_name = b.constraint_name " +
                                        "   AND a.constraint_type IN ('R', 'P') " +
                                        "   AND b.column_name = :columnName " +
                                        "   AND a.constraint_type = 'P' ";

        const string GET_FOREIGN_KEY_SQL =  "SELECT d.table_name " +
                                            "  FROM all_cons_columns c, all_cons_columns d, all_constraints e " +
                                            " WHERE d.constraint_name = e.r_constraint_name " +
                                            "   AND c.constraint_name = e.constraint_name " +
                                            "   AND d.column_name = :columnName " +
                                            "   AND e.table_name = :tableName ";
       
        const string GET_PRIMARY_KEY_SQL =  "SELECT e.table_name AS TableName, c.column_name AS ColumnName " +
                                            "  FROM all_cons_columns c, all_cons_columns d, all_constraints e " +
                                            " WHERE d.constraint_name = e.r_constraint_name " +
                                            "   AND c.constraint_name = e.constraint_name " +
                                            "   AND d.table_name = :tableName ";

        #endregion

 

So, what's the deal...

All we needed to change was the "From" sections to indicate that we want to query ALL objects and not just the USER objects.  You may need to work with your DBA's in order to get this additional level of access however, it will allow you get around the issues with schema ownership in oracle.

Oh, by the way...

You will also want to make sure you use the IncludeTableList or ExcludeTableList options within your config to narrow down your objects.

 



kick it on DotNetKicks.com

Schrödinger's cat

As taken from The Pragmatic Programmer (Hunt & Thomas):

Suppose you have a cat in a closed box, along with a radioactive particle.  The particle has exactly 50% chance of fissioning into two.  If it does, the cat will die.  If it doesn't the cat will be okay.  So, is the cat dead or alive? 

According to Schrödinger, the correct answer is both.  Every time a sub-nuclear reaction takes place that has two possible outcomes, the universe is cloned.  In one, the even occurred, in the other it didn't.  The cat's alive in one, and dead in another.  Only when you open the box do you know which universe you are in. 

Think of code evolution along the same lines as a box full of Schrödinger's cats: every decision results in a different version of the future.  How many possible futures can your code support?  Which ones are more likely?  How hard will it be to support them when the time comes?

Dare you open the box?

System.Web.Security.MemoryLeakProvider

System.Web.Security.MemoryLeakProvider
Have you enabled yours yet?  I have and boy what a waste of time this bugger was. 

Symptoms:
Enabling the ASP.NET membership provider via web.config...
<roleManager enabled="true"...
resulted in some very bad behavior in memory.  On my default page (like many pages), I had some code that simply checked that the user was logged in and if they had a couple "flags" set in their profile.  However, when the application went through QA we saw memory spiking through the roof.

I immediately thought - open data readers or connection objects.  I was wrong, especially when I stumbled onto this article:
http://msdn.microsoft.com/msdnmag/issues/06/07/WebAppFollies/.

Uncached Roles


The following statement is frequently found in the web.config files of ASP.NET 2.0 applications and in samples introducing the ASP.NET 2.0 role manager:

<roleManager enabled="true" />
As presented, however, this statement can have a demonstrably negative impact on performance. Do you know why?

By default, the ASP.NET 2.0 role manager doesn’t cache roles data. Instead, it consults the roles data store each time it needs to determine which roles, if any, a user belongs to. This means that once a user is authenticated, any page that utilizes role data—for example, pages that use site maps with security trimming enabled, and pages to which access is restricted using role-based URL directives in web.config—causes the role manager to query the roles data store. If roles are stored in a database, that’s one more database access per request that you can easily do without. The solution is to configure the role manager to cache roles data in cookies:

<roleManager enabled="true" cacheRolesInCookie="true" />
You can use other <roleManager> attributes to control the characteristics of role cookies—for example, how long the cookies should remain valid (and consequently how frequently the role manager will go back to the roles database). Role cookies are signed and encrypted by default, so the security risk, while not zero, is mitigated.


kick it on DotNetKicks.com

Are programmers at a greater risk of cancer?

Have you ever just sat there and thought "gee Dave, I've been looking at a computer screen for 12 hours straight.  Why do my eyes hurt?"

So, the thought occurred to me this morning that perhaps programmers, or just anyone who uses the computer for hours on end every day, is causing a great deal of stress on their eyes.  I'm not a doctor but from everything you hear with other types cancer, stressful external influences on the body tend to have negative long term effects.

Does this mean that using the computer all day every day raises your risk of developing a cancer variant of the eye;  such as Intraocular Melanoma

Why ASP.NET message board systems have failed

There comes a time when one must accept that one design is better than another.  Lets use an example with cars:

Ford and Nissan both build a minivan.  They both are targeting a market that demands a car that can meet the perceived requirements of the target customers.  Now, is the measurable based on the profit that the company makes?  Or, whether the customers repeat their purchase for a newer version of the vehicle?  Or, how many vehicles are sold?

In software, I measure a product's success by retention it gains with it's customers.  That is, if customers come back to continue to purchase your product.  In the realm of open-source and free software, the % of people that use your software versus a competing product is a more valid estimation.

This is why asp.net based message board systems have failed.  According to bigboards.com, 83% of the largest message boards on the internet are hosted with php-based solutions.  The most common being vBulletin followed by Invision and phpbb. Why?  Let's compare the perceptions of these systems versus a well-known and popular asp.net based community system - Community Server.  I use Community Server as an example because I think it fits the mold of exactly why the ASP.net systems have not worked out.
  • Installation: Tie - all of the systems come with easy installers and most of the documentation is well written.
  • Performance: php - The php systems simple work faster.  Most of the time this is because they don't implement fancy Ajax scripts, rich text editors, and other slow rendering "features".
  • Customization: php - The php systems simply do not require you to learn a proprietary customization system.  You dig right in and you can get to anything you need.
I could go further but those serve the major points I am making.  The speed and ease of use of the php based systems is why over 80% of the largest forums on the internet are hosted with php based message boards.

What Messages Boards Need To Be Successful

  • Easy Installations that are not frustrating and take little time to complete.
  • Performance
  • Productivity features that don't sacrifice performance.

What Messages Boards Don't Need

  • Fancy rich-text editors
  • Ajax pages that take forever to load and deliver little benefit
  • Complicated user interfaces that are not intuitive and hard to use
If your thought is that I am saying "php is better than asp.net", you are missing my point.  Users want a simple message board that works and doesn't frustrate them.  If the extra "features" you added to attract users sacrifice performance and add no real value (something users actually use, no pun intended), then remove the "feature".

kick it on DotNetKicks.com

Software Development - It's like ice cream, only better

Yes, this is yet another software development analogy.  Sue me.

So, software development is like ice cream.  Ice cream has many flavors, chocolate happens to be one of my particular favorites.  I'm also not the only one either, many people like chocolate ice cream.   In fact, a LOT of people like chocolate ice cream.  But there are those other people who don't.  Those who seek to destroy the chocolate ice cream-loving people and replace chocolate with...vanilla.  Or perhaps mint chocolate chip, cookies and cream, butter pecan, or maybe even cookie dough.  You could probably categorize everyone based on their favorite flavor of ice cream.  Sure, not everyone likes just one flavor however; most people can get down to one or two flavors they really like.

The analogy.
Take a second and think about your day-to-day practice as a software developer.  You probably work with other developers at some level or at least make use of code from other developers on your projects.  You also know that, syntax aside, the code is always different.  Joe's code looks like Joe's code and no other developer in the world has code that is exactly like Joe's.

Like ice cream, everyone develops code that has it's own flavor.  With these flavors, developers seem to pool towards their own unique style.  For example, vanilla ice cream might represent test driven development.  On the flip side, chocolate ice cream might represent bug driven development, TDD's evil half sister. 

Mixing it up.
We know that chocolate and vanilla are two of the base flavors used when creating other flavors.  For example, we might be doing TDD (vanilla) with a project written in .NET (chocolate chips) using SubSonic (mint).  I happen to be a big fan of Mint Chocolate Chip ice cream and I know that a lot of other people are as well.  Not everyone however; as some people like to just stick with Chocolate Chip TDD .NET development.

Furthermore, we can relate this theory to software development teams.  A team of developers has pockets within it, no matter how big or small.  The lead developer (or architect) says "We will be eating Mint Chocolate Chip ice cream for this project."  A lot of the other team members quiver as they are not fans of the Mint Chocolate Chip development practice.  So, even though you are developing in Mint Chocolate Chip, some elements of butter pecan start to work their way in.  Then some chocolate ice cream starts to show up as well as maybe some orange dream. 

Often, at the end of the day you end up with a medley of flavors that very few people like and very few people will even bother to try.  Sometimes it's brilliance, other times it's a complete disaster but your software product's flavor (it's appeal to the customer) is always a result of the team's taste. 

I will leave it up to you to figure out the rest.

kick it on DotNetKicks.com

Visual Studio, References, and You

One of the things that always dogged me for a while with Visual Studio (Both 2003 and 2005) was how references were handled.  Until recently it was one of those mysteries that no one ever really mentioned or spoke openly about.  Like a sore topic you never bring up, you really have to be looking for specific stuff to find the answers to the riddle.

It dawns on you when you realize how simple it is.  The rest of this article is not about how it actually works rather, how to make your life easier when working with references.

An ASP.NET Scenario


In a typical asp.net application you may have one or more references to additional non-framework libraries (dll's).  That thought is simple enough however, the reality is not always that simple.  As you know, these references are resolved within a refresh file.  A typical refresh file has a path to the reference as in this example:

C:\Program Files\Microsoft ASP.NET\ASP.NET 2.0 AJAX Extensions\v1.0.61025\AJAXExtensionsToolbox.dll

Now, this is fine until you try and deploy this to a remote computer or a web server.  Furthermore, it becomes an even bigger headache when you talk about deploying the application from another computer other than your own; such as a build server.  Obviously on the build server, having all of the possible components installed is not always viable.  Nor is having them installed in the same places on all of your servers or computers.

Build and Deployment Management


At the onset of a new project, an idea of what the final state of your application's existence should be in mind.  Correcting the above issue early is key to eliminating the headaches.  What we do in applications is place any references used in the application into a folder that is brought down with any version.  That ensures that we can reference our dll's from a reliable location within our application each time we build it.

Managing The Refresh Files


The next aspect of controlling the references is to make sure that your references are pointed to your reference folder and not the installation path of your component.  You can adjust these either by using the Visual Studio GUI or by editing the refresh file manually.  In this case, your reference from the example above now becomes:

..\..\..\lib\AJAXExtensionsToolbox.dll

It's not rocket science but it is a bit frustrating to deal with if you never have dealt with it before.  One of the common problems that comes up is that often times our tools have snap-ins for the Visual Studio toolbox.

Be Careful Of The Toolbox


When you drag and drop items from the toolbox in Visual Studio, it will automatically update the references in your application as well as the .refresh files which map to your references.  This means it will revert any work you have done to resolve conflicts on other computers. 

There is no easy way around this.  You can trick the system by setting all of your .refresh files to read only.  In cases of applications under source control, this is often done for you, such as under Source Safe.  However, you will still be prompted to check out the .refresh files automatically and all cases I just tell it to cancel the operation.  It, of course, barks at you but it is a lot lesser than having it screw up all of your references.

I hope that in future version of Visual Studio a better solution can be found.
But I won't hold my breath.
kick it on DotNetKicks.com

SubSonic Events Preview

For a while now I have been working on a set of improved templates for SubSonic 2.0.3.  One of the things that I have gradually found more use for is .NET Events and event handler.  More specifically I found myself yearning for the ability to have my SubSonic classes raise events when certain actions are taken upon them.

For example, if I create a new employee record and I would like to execute some parallel processing without needed to sync back up with my post-back, I may want to have an event handler for my class that is fired whenever a record is inserted. 

So, over the past week I have been at work on SubSonic Events.  Here is an idea of the Pseudo-code for the events.

Public WithEvents emp As Employee
Public Sub Page_Load()
emp = New Employee(13)
End Sub

Public Sub emp_Created (NewRecord As Employee) Handles emp.Employee_Created
'--- Do some processing here with the New Record
'--- This could also be done in parallel within the MVC object
End Sub


kick it on DotNetKicks.com

The World's Most Over-Thought Redirect

Let me start by saying, I like the guys over at Microsoft.  However, every now and then you catch them doing something that just makes you scratch your head and wonder....why?

I was simply looking for the Northwind database.  I was unaware that they moved them to Codeplex - not a bad idea.  But I found out by google sending me to this page.  Now, instead of just redirecting you to the website, they give you a pdf file that looks like this; truly the case of a great idea gone way too complicated.

Free Image Hosting at www.ImageShack.us

kick it on DotNetKicks.com

Flowcharts Are Cool

Flowcharts are cool and don't get enough love. I love them because I am a visual person.  I don't learn (as well) from reading books as I do from getting into the guts of something, tearing it apart, and putting it back together.  I do the same with cars, computers, and pretty much anything else I like to toy with.  The thing I love about flowcharts is that you easily think out a solution in a matter of 15 minutes by just putting something together in Visio.  Not to mention you establish a "screen shot" of your brain for other developers to pick up on and understand just what the hell you were thinking when you wrote X module.

They are so great, you can even depict your work day with them.


Visual Studio Gripes!!!
Yes, we all have them.  Visual Studio, this morning, decided it would randomly start modifying my web.config on a project that has been under development for over a month.  I got to thinking, wouldn't it be nice for Visual Studio to actually tell you what it is trying to modify rather than just saying "Yo dude, I gots ta' modify your web.config.  Dat' be alright?"

It would be the equivalent of an automotive repair shop telling you "Hey, before you can start your car up, we need to modify the computer on the car."  Wouldn't you want to know what was trying to be modified rather than just saying "Yea sure, thats fine."

Maybe I am asking for too much here.
kick it on DotNetKicks.com
«July»
SunMonTueWedThuFriSat
293012345
6789101112
13141516171819
20212223242526
272829303112
3456789