Friday, March 21, 2008

I've been working for two hours to get my existing NetTiers CodeSmith Template to generate the files the way I need them. The NetTiers template gets 90% there out of the box but the template tips for each line item are sometimes obsure or require another lineitem to be TRUE. Then there is a fair bit of user error such as what to name the libraries created by the template, the directory they should be sent to. One error right out of the box is the LibraryPath value. It's inconviently stuffed in the Advanced CRUD section and just says Reference. After the projects try to build but fail but to the reference libraries not found, I figured out what was going on.

NetTiers generates the web service and client pieces which I keep in the template but don't use.

No I'm working on making sure my project library which includes all the libraries built by NetTiers CodeSmith has the right hierarchy in terms of having functions where I would expect them to be.

I'm sure at some point I'll actually get to the point where I call some of these great new classes.

Friday, March 21, 2008 8:26:32 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, March 20, 2008

I have met several local business people that either want or need a business web site beyond a brochure site. This post is aimed at those people so this category is not for techies per say but is a forum to discuss the client side of the business.

Let's assume the web client needs to be able to

  • Introduce Company and People
  • Provide contact information and previous work bonafides
  • Provider specific and detailed information about the products or services they provide to local clientele
  • Provide access so that company person can manage site's information
  • Show enough design thought that customers don't think someone's eight-year old just learned HTML

So why pay a programmer (like me) upwards of $60/hour for this. This is basically a web site builder. You can buy this for around $10/month with more features and designs than you need. What skills do you need to be able to do this yourself?

  • Able to use a computer browser
  • Able to type/edit on keyboard
  • Able to read and respond to email

The downside of this, and it's huge for any small business owner, is that now there is one more thing you need to take care of. Is it worth it to hire another person to do your typing when there are changes? Sure, but that's not a web programmer or web designer. That's usually called a secretary and they are inexpensive compared to a programmer. Always ask yourself are you hiring the right person for the job.

Dina | SYWYOW
Thursday, March 20, 2008 6:46:27 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Wednesday, March 19, 2008

So where am I with my Pet Project?

 

  • the domain name(several really) purchased
  • domain names linked to the web server
  • email entries set up
  • new virtual development server (Thanks Wayne)
  • VS2005 web project with a middle layer library
  • SQL Server database for project
  • SQL Server database for bugtracker/issue mgmt
  • CodeSmith
  • .NetTier with a template close to what I need
  • basic but ugly functionality
  • using Microsoft Membership, Roles (Profiles are home grown)
Wednesday, March 19, 2008 6:17:02 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Tuesday, February 26, 2008

Here is a little code that will create a class that you can use with Enterprise Library caching.  Basically it allows you to define a WHERE clause where any object in that WHERE clause changes (across a single table) then the cache will expire the object in the cache.  Think of it as a way to store a List<> of object that represent rows in a table in the cache and be able to detect if any row changes so that you can drop the cached list.  LastUpdate is a timestamp column that needs to be on every row.  This works since timestamp column type is always incremented by one, which means that SELECT MAX(LastUpdate) will tell you the maximum change across all rows.

using System;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data;
using System.Configuration;

using Microsoft.Practices.EnterpriseLibrary.Caching;
using Microsoft.Practices.EnterpriseLibrary.Caching.Properties;

namespace ERS.Base
{
    /// <summary>
    /// 
    /// </summary>
    public class TableSqlDependency : ICacheItemExpiration
    {
        String _tableName = String.Empty;
        String _connectionString = String.Empty;
        String _where = String.Empty;
        Byte[] _lastUpdate = null;

        /// <summary>
        /// 
        /// </summary>
        /// <param name="connectionString"></param>
        /// <param name="tableName"></param>
        public TableSqlDependency(String connectionString, String tableName)
        {
            _connectionString = connectionString;
            _tableName = tableName;
        }

        public TableSqlDependency(String connectionString,
            String tableName,
            String where)
        {
            _connectionString = connectionString;
            _tableName = tableName;
            _where = where;
        }

        // Summary:
        //     Specifies if item has expired or not.
        //
        // Returns:
        //     Returns true if the item has expired, otherwise false.
        public bool HasExpired()
        {
            Byte[] lastUpdate = LastUpdate;

            if ((lastUpdate == null) && (_lastUpdate != null))
                return (true);

            if ((lastUpdate != null) && (_lastUpdate == null))
                return (true);

            if ((lastUpdate == null) && (_lastUpdate == null))
                return (false);

            if (lastUpdate.Length != _lastUpdate.Length)
                return (true);

            for (int i = 0; i < lastUpdate.Length; i++)
                if (lastUpdate[i] != _lastUpdate[i])
                    return (true);

            return (false);
        }

        private Byte[] LastUpdate
        {
            get
            {
                Byte[] lastUpdate = null;

                using (SqlConnection sqlConnection =
                    new SqlConnection(
                        ConfigurationManager.ConnectionStrings[_connectionString]
                        .ConnectionString))
                {
                    String sql = String.Empty;

                    if (!String.IsNullOrEmpty(_where))
                        sql = String.Format(
                            "SELECT MAX([LastUpdate]) 'Max' FROM [{0}] WHERE {1}",
                            _tableName, _where);
                    else
                        sql = String.Format(
                            "SELECT MAX([LastUpdate]) 'Max' FROM [{0}]",
                            _tableName);

                    using (SqlCommand sqlCommand =
                        new SqlCommand(sql, sqlConnection))
                    {
                        sqlConnection.Open();

                        sqlCommand.CommandType = CommandType.Text;

                        using (SqlDataReader sqlDataReader =
                            sqlCommand.ExecuteReader())
                        {
                            if (!sqlDataReader.HasRows)
                                lastUpdate = null;

                            sqlDataReader.Read();

                            lastUpdate = (Byte[])sqlDataReader["Max"];
                        }
                    }
                }

                return (lastUpdate);
            }
        }

        //
        // Summary:
        //     Called to give the instance the opportunity to
        //        initialize itself from information
        //     contained in the CacheItem.
        //
        // Parameters:
        //   owningCacheItem:
        //     CacheItem that owns this expiration object
        public void Initialize(CacheItem owningCacheItem)
        {
            _lastUpdate = LastUpdate;
        }

        //
        // Summary:
        //     Called to tell the expiration that the CacheItem to which
        //        this expiration
        //     belongs has been touched by the user
        public void Notify()
        {
        }
    }
}

{6230289B-5BEE-409e-932A-2F01FA407A92}

.net | C# | T-SQL | Wayne
Tuesday, February 26, 2008 10:44:49 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, February 18, 2008

I'm getting farther along in my new pet project. I had many issues with converting to a web application project. Then I balked at which style of data access/data layer to use. Then I fidgetted with notation and styles. After wasting bunches of time, I'm finally moving along. Data is going in, data is going out. It looks aweful and the backend is a SQL client. But the user functionality is moving along. The rest can be fixed after I feel some momentum.

So I'm using asp.net 2.0 c# with sql backend. Enterprise Framework from MS patterns and practices. MS Membership provider with a SQL provider. Data layer is business objects with provider model. I have nunit plugged in but haven't written in unit tests yet. Argh!

Since I'm not a graphic designer, it looks BAD. A bad ripoff of www.xcache.com.

 

Programming to www.pandora.com.

 

ASP.NET | C# | Dina
Monday, February 18, 2008 10:53:19 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Friday, February 15, 2008

http://www.15seconds.com/issue/080214.htm - Create a Slide Show Using the AJAX SlideShow and TreeView Controls

ASP.NET | C# | Dina
Friday, February 15, 2008 7:45:19 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 

For the longest time I couldn't skin my custom control, background: a custom control looks like this:

public class MyGridView : GridView
{
...
}

This custom controls was in a separate class library.  Bill researched the problem and it appears that you can't skin a custom control that is in a nested namespace.  I.e. the custom control can't have this:

namespace MyNamespace.SubNameSpace
{
   public class MyGridView : GridView
   {   
   ...
   }
}

It has to be in a single namespace to work:

namespace MyNamespace
{
   public class MyGridView : GridView
   {  
   ...
   }
}

Visual Studio 2005 - .NET 2.0 - ASP.NET 2.0
{6230289B-5BEE-409e-932A-2F01FA407A92}

 

ASP.NET | C# | Wayne
Friday, February 15, 2008 9:16:58 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  | 
 Thursday, February 14, 2008

My office mate Chad came up with yet an even better option. You need to be using 3.5 and include the System.Linq extension method namespace. It didn't perform quite as well but pretty close. Even more general purpose. Note that the default SequenceEqual method throws an exception when either operator is null so I handled those cases first.

bool Compare<T>(IEnumerable<T> left, IEnumerable<T> right)

{

    // handles the same array

    // handles both null

    if (left == right)

        return true;

 

    // fails when either are null

    if (left == null || right == null)

        return false;

 

    return left.SequenceEqual(right);

}

 

Andy | C# | LINQ
Thursday, February 14, 2008 3:02:02 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 

Slightly more efficient by starting off with a reference compare. If the same two arrays are passed in, no need to compare the elements. I don't think there is a better way unless you use unsafe code / pointers. The use of generics makes this more general purpose.

bool Compare<T>(T[] left, T[] right)

{

    // handles the same array

    // handles both null

    if (left == right)

        return true;

 

    // fails when either are null

    if (left == null || right == null)

        return false;

 

    if (left.Length != right.Length)

        return false;

 

    for (int i = 0; i < left.Length; i++)

    {

        if (!left[i].Equals(right[i]))

            return false;

    }

 

    return true;

}

 

Andy | C#
Thursday, February 14, 2008 1:03:17 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 

Here is a little code that I simplified:

public Boolean Compare(Byte[] a, Byte[] b)
{
   return(a=b);
}

Seems like it should work right?  Compare each Byte in a with the same Byte position in b and return if the two arrays have equal values in each position?  It doesn't do that -- it compares the two objects to see if they are equal.  My solution:

public Boolean Compare(Byte[] a, Byte[] b)
{
    if ((a == null) && (b != null))
        return (false);

    if ((a != null) && (b == null))
        return (false);

    if ((a == null) && (b == null))
        return (true);

    if (a.Length != b.Length)
        return (false);

    for (int i = 0; i < a.Length; i++)
        if (a[i] != b[i])
            return (false);

    return (true);
}
Is there a better solution?
{6230289B-5BEE-409e-932A-2F01FA407A92}

 

C# | Wayne
Thursday, February 14, 2008 8:50:49 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 

In working on some inherited code in a very large and complex website I was a little bit skeptical of touching the code.  Let's just say the codebase was dubious at best.  This presented a problem as I had 3 days to make a lot of changes in the admin section of the site.  When inheriting code there is always a learning curve.  Unfortunately I didn't have this luxury as I spent 1.5 days getting the code running locally (still haven't COMPLETELY finished this).  This left me with about another half-day to learn the handful of tables I was to be using.  One day to actually write the code.  No problem. 

So the client relayed to the PM what needed to be done which was ultimately relayed to me.  "When a manager selects a value from the whatever dropdownlist the values for the FileUpload control disappears and the user has to re-enter this value (which is by design as this would be a huuuuge security risk. Think if you could programmatically set the postedfile for the FileUpload control!).  Different sections are displayed depending on the value of such-and-such dropdownlist, etc..." I then ask myself, "WTF are there so many postbacks to simply hide / show sections?"  This sounded  like a perfect opportunity to utilize my new favorite property: Control.ClientID.  The goal was to move the functionality from server-side to client-side while making minimal changes to the server-side code ( VB* ;-) ). Half of the dropdownlists had AutoPostBack enabled.  I expected to see a handful of handlers when I viewed the code-behind. Nope. Why is it that developers feel the need to always post back to the server even if nothing is happening? Maybe they were planning to implement the handlers in the future?  A postback is kind of a big deal and I believe they should be used sparingly, but that is just me.  Anyway, my server-side code consisted of maybe 6 lines where I added Attributes to server-side controls similar to:

dropdownlist.Attributes.Add("onchange",

   string.Format("clientSide('{0}', '{1}', this.value)",

   someDiv.ClientID, anotherDiv.ClientID))

With ClientID I am shoving the ID of the rendered control into my dropdownlist's onchange event so I can guarantee the control Id on the client-side.  This is extremely important since this particular site uses a series of nested masterpages and you can never be too sure what the output control id will be.  I then wrote some utility JavaScript functions to set CSS display style to "none" or "" depending on the values selected.  I also use client-side JavaScript to enable / disable appropriate validators (which is pretty cool).

ValidatorEnable("validatorId", false); // disable
ValidatorEnable("validatorId", true); // enable

It may seem like a bit more work solving problems on the client-side, but in the end it definitely makes the user experience more fluid.

* I have found that VB can be quite pleasant when written correctly.  I am not the definitive authority on correct programming but I'm pretty sure immuting a string object while enumerating an ArrayList with a series of nested if statements and for loops is not the correct way to display SQL data in tabular form.  I also can't think of a good reason to have 400+ lines in Page_Load. These practices aren't language-specific and would probably piss me off in any .NET platform.

Thursday, February 14, 2008 7:17:59 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |