2012-06-23

Entity Framework v. Data Access Layer

A recent undertaking of mine involves a paradigm move into a different framework. My traditional approached, in the IIS/ASP environment, has been to use ASP.NET rendering with the traditional HTML/C#.NET or VB.NET code-behind. Instead, I have a project that is based on MVC, or more specifically, Microsoft's MVC framework in ASP.NET.
MVC is an acronym for Model-View-Controller. This paradigm is a class-centric view of the typical client-server model. For instance, in ASP.NET, the markup and code-behind draw a division that is hardware-centric. The concept of MVC is scalable - it can be applied to an individual control as well as an entire interface. In this overall concept, the Controller receives the requests from external actors and interprets those requests as modifications to the data (Model) or the View or both. Applying this to a single textbox control gives the focus/blur and keyboard handler to the controller; the text buffer is the model, with the size and other rendering delegated to the view.
Microsoft's MVC is not so conceptual. It is a specific implementation of the MVC concept to web renderings at the client-server level. In this case, a web site may contain a series of models. The input marshalling to these controllers is controlled through a router, which is largely hidden. The router operates on a subscription basis, which is in contrast to the discovery protocols used with the network devices that bear the same name. The subscription is caused by the signature of methods within the controller.
Though the View can be rendered using ASP.NET controls, MVC also has a new rendering engine, called Razor. This engine allows the user to more fluidly mix markup, Javascript, and code-behind. There is a price for this: Microsoft gets to guess what it's currently parsing. In my short stint using Razor, I've come across two issues with rendering abnormalities. The first was parentheses pairs in the data were being interpreted as Javascript function calls. Replacing the parens with brackets fixed that. The other involves Razor's string sterilization. By default, Razor will strip all potential cross site scripting (XSS) threats from strings. This can be specifically overriden for a given control, but there is no partial whitelist/blacklist for it. Instead of the familiar asp: controls, Razor has a parade of helper methods for control rendering. It also makes substantial use of jQuery. Despite some quirky issues with bad guesses, Razor seems to allow a code structure that is more closely related to the Document Object Model (DOM).
This brings us to the Model. In its simplest form, the Model is just a class with properties and no methods - a struct. From this, you could construct a data access layer in the controller to manipulate the Model properties directly through a familiar SQLClient class. However, my particular project did not afford such familiarity; the original designers decided (did I mention that I inherited this project - well, I did) to use the acronym coupling of EF (Entity Framework) on ADO (ActiveX Data Object) using Linq for Entities, and this is the crux of this post. Instead of a very simple set of static methods in a class of their own to use as an API, I have spaghetti. Nicely frameworked spaghetti, but spaghetti none-the-less. The primary issue is the degree of overlap between the Entity Framework and the actual database. Changes must be accounted for on both sides, instead of just at the SQLCommand/Stored Procedure interface. Linq for Entities brings its own problems to the party, as well. It is possible, between MVC's use of lambda equations and Linq for Entities' obfuscation, to generate a SQL call that has no direct reference in the code.
Before this becomes a rant - avoid EF and Linq for Entities. They bring nothing new, other than obfuscation headaches. As for the rest of Microsoft's MVC on ASP.NET implementation - the jury is out. I'll see how this project progresses.

-- Steve

2012-03-24

Code Patterns: IndexOf()

The IndexOf() String method in C# and VB.NET should be accompanied with an "if" statement as a matter of practice.  There are some exceptions, and the absence of the conditional should be explained in the comments around that code section.  Here are two examples demonstrating a code section using IndexOf() in C#:

    // IndexOf() with trinary conditional
    String strTest = "123456, 7890";
    int intCommaPosition = strTest.IndexOf(',');
    if (intCommaPosition > 0)
    {
        // stuff to do if the comma is found
    }
    else if (intCommaPosition == 0)
    {
        // stuff to do if the comma is the first char
        //  typically, this is an unusual condition
    }
    else
    {
        // stuff to do if the comma is not found
    }// of trinary if-else if on IndexOf() return value

    // IndexOf() with binary conditional
    intCommaPosition = strTest.TrimStart(',')

                          .TrimStart().IndexOf(',');
    if (intCommaPosition > -1)
    {
        // stuff to do if the comma is found
    }
    else
    {
        // stuff to do if the comma is not found
    }// of binary if on IndexOf() return value


The trinary can intercept the condition where the character leads the string.  The binary removes leading characters like the one we are looking for, then any leading whitespace, and finally it looks for the character of interest.  This way, the binary conditional's intCommaPosition calculation removes the unlikely instance that the string strTest starts with a comma.

This example uses C#, though VB.NET is similar.  Please remember to force the constants to characters if using Visual Basic.  Here's an example:

    Dim strTest As String = "123456, 7890"
    Dim intCommaPosition As Integer = 0

    ' uses variant 4 of 9 - IndexOf(String)
    intCommaPosition = strTest.IndexOf(",")
    ' uses variant 1 of 9 - IndexOf(Char) - faster
    intCommaPosition = strTest.IndexOf(","c)
    ' ... if statements! ...


As a programmer, I discovered this back in 1971, using InStr$() functions with Basic.  Even though those days had me coding in a high school closet using a 75 baud ball-and-hammer teletype through a 300 baud acoustic coupler into a telephone line that had a good deal of crosstalk as it traversed the city to the University of Pittsburgh, with persistent storage on paper-clipped paper tapes fashionably tucked into my dress shirt pocket, the code pattern remains: do not assume that the character will be found!

Sorry for the geek visual; it was a fun time in those days, and it still is fun now.
-- Steve

2012-02-25

Code Patterns: Setting Booleans

This comes up often enough that it deserves some thought: setting booleans.  The two most used booleans are the .Visible and .Enable properties available on all UI Forms and Web Forms controls from Microsoft.  There are four forms for setting these (a and b are of similar type; bc is a boolean):

In C-ish:

        // form one
        if (a == b)
            bc = true;
        else
            bc = false;

        // form two
        bc = false;
        if (a == b)
            bc = true;

        // form three
        bc = (a == b) ? true : false;

        // form four
        bc = (bool)(a == b);

In Visual Basic:

        'Form one
        If a = b Then
            bc = True
        Else
            bc = False
        End If

        'Form two
        bc = False
        If a = b Then bc = True

        'Form three
        bc = If(a = b, True, False)

        'Form four
        bc = CBool(a = b)

I advocate form four in both languages.  Without the "If" keyword, directive, or function, there is no zero-bit check in the assembly with the subsequent code jumps.  Even if the compiler is efficient and recodes these scenarios, then I still favor form four, because it is concise. 

I run across this frequently when I'm reviewing code, and I hope that this may remove one more rewrite in future reviews.

-- Steve

2012-02-20

TSQL DISTINCT qualifier

Using the DISTINCT qualifier in the SELECT clause typically causes a late sort in SQL Server's execution plan.  Since that sort contains duplicated rows, it is inefficient.  There are alternatives that may be more efficient.
1. Using GROUP BY - this strategy uses GROUP BY across all of the items in the SELECT clause.  Duplicates will be aggregated, with only one occurrence in the result set.  The execution plan must be verified; specifically, that the late DISTINCT SORT has now moved to an earlier SORT.
2. Using the EXISTS() function - This works best if the items in the SELECT clause are from one table.  It can sometimes be done across multiple tables, though I don't know of a generalized form for re-writing the multi-table query. In the single-table case, the form is:

SELECT DISTINCT S.c1, S.c2, ..., S.cn
FROM tableName1 S
    JOIN tableName2 T ON S.i1 = T.i1
    ...
WHERE (... other constraints ...)

<<  which  becomes  >>

SELECT S.c1, S.c2, ..., S.cn
FROM tableName1 S
WHERE EXISTS(SELECT 1
  FROM tableName1 S2 
     JOIN tableName2 T ON S2.i1 = T.i1
     ...  
  WHERE S.PK = S2.PK
     AND (... other constraints ...) )


The efficiency of this re-write is best demonstrated by the actual execution time of the query.  The execution plan doesn't have a way to show that the query within the EXISTS function is actually a series of short-circuit ORs.  Once any one is made, it does not need to further evaluate the condition.  The PK field in the above query represents the primary key field of table S.

-- Steve

2012-01-29

OpenSUSE 11.4 kernel build HD space

At 8:30am I performed my first complete kernel build on Linux (2.6.37).  I've had to repartition twice to get to this point, so I thought I'd post some advice.  The kernel build (make rpm) used 5.3 G Byte of hard drive.  This was on root (/), since I did not have any mount points in the path /usr/src/.  This was openSUSE 11.4 and CONFIG_DEBUG_INFO was set to Y.  Basically, I only made one change in make menuconfig, and that was the text to append to this build's name.  Please plan accordingly for your specific build.
-- Steve

2012-01-27

Entrance

A journey begins with a single thought - and this one will suffice.