Wednesday, August 20, 2008

So we did all this work to a membership provider and a role provider for ASP.NET and it was a lesson in patients as we figured out how it was all suppose to work.  However, now we have it I found out that the ASP.NET controls are not enabled for roles.  It works well with our Forms authentication for blocking access to pages where the user doesn't have the right role.  However, I want to display controls and not display controls based on the user's role.  I love to create custom controls that work just like we want them to work.  So I subclassed the Panel control and "roles" enabled it.  Code:

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Text;
using System.Web;
using System.Web.UI;
using System.Web.UI.WebControls;

namespace WebUtility
{
    [DefaultProperty("Text")]
    [ToolboxData("<{0}:RolePanel runat=server></{0}:RolePanel>")]
    public class RolePanel : Panel
    {
        /// <summary>
        /// Gets or sets the roles
        /// </summary>
        /// <value>The on click.</value>
        [Bindable(true)]
        [DefaultValue("")]
        [Localizable(true)]
        public string Roles
        {
            get
            {
                String s = (String)ViewState["Roles"];
                return s ?? string.Empty;
            }
            set
            {
                ViewState["Roles"] = value;
            }
        }

        protected override void OnLoad(EventArgs e)
        {
            Boolean bFound = false;
            foreach (String role in Roles.Split(','))
                if (Page.User.IsInRole(role.Trim()))
                {
                    bFound = true;
                    break;
                }

            if ((Visible == true) && (bFound == false))
                Visible = false;

            base.OnLoad(e);
        }
    }
}

Now all I have to do is to all this ASP.NET code to our pages:

<webutility:RolePanel runat="server" ID="adminRoleHyperLink" Roles="AdministrationAccess">Admin</webutility:RolePanel>

The new Roles property is a comma delimited list of "roles" that are checked against the users permissions when the page loads, if they have one role in the list they can see everything in the panel.

Note that the same code will work for most all ASP.NET controls without changing it, just modify the name of the control you are subclassing.

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

 

 

ASP.NET | C# | Wayne
Wednesday, August 20, 2008 9:29:28 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Tuesday, August 19, 2008

Last post was about how dynamic javascript in the update panel was not being re-executed on a partial postback.  I have a better way of making it execute -- parse the controls in the UpdatePanel and call the javascript method eval().

Here is the hook:

Page.ClientScript.RegisterStartupScript(this.GetType(), "LoadHandler", "function LoadHandler(){EvalScript($get('" + updatePanel.ClientID + "'),'scriptId');};\r\n", true);

Page.ClientScript.RegisterStartupScript(this.GetType(), "AddLoadHandler", "Sys.Application.add_load(LoadHandler);\r\n", true);

EvalScript looks like this:

function EvalScript(control, id)
{
 if(!control){return;}
 for(var n=0;n<control.children.length;n++)
 {
     if((control.children[n].tagName == 'SCRIPT') && (control.children[n].id == id)){
         var scriptElement = control.removeChild(control.children[n]);
         eval(scriptElement.innerHTML);
     }else{
         EvalScript(control.children[n], id);
     }
 }
}

Basically EvalScript searchs client side all the controls in the updatepanel control for one with the name specified, then calls Eval on it's innerHTML.  Just a note, you don't want inside comments in your <SCRIPT> tag, they don't eval() well.

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

AJAX | ASP.NET | Wayne
Tuesday, August 19, 2008 12:06:39 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Friday, August 15, 2008

If you are using an Ajax UpdatePanel in ASP.NET and you have a control that renders javascript, that javascript will not change when on a partial postback.  For example, I have a Repeater control that loops though so GEO points and outputs javascript to add PushPin to a Virtual Earth map.  If a partial postback is called, and the Repeater rebinds it recreates the javascript, however on the client side the ScriptManager just replaces the UpdatePanel innerHTML with then new code.  It doesn't tell the javascript engine to reparse that code and recreate the functions that are being replaced.  To solve this problem I had to write some javascript client code that would parse the return, and move my <SCRIPT> tags to the head of the page.  This "woke up" the javascript engine and the new functions where recompiled.

How to hook it up:

1) The first thing to do is to tell the ScriptManager to parse the innerHTML of the UpdatePanel and move all script blocks to the head of the page (note: you need to have a page head).  You can do this like this:

Sys.WebForms.PageRequestManager.getInstance().add_endRequest(EndRequestHandler);

2) Next you need to add a function that call EndRequestHandler that will call the parser, like this:

function EndRequestHandler(sender,args){ParseScript($get('UpdatePanelId'));};

Mine looks like this:

Page.ClientScript.RegisterStartupScript(this.GetType(), "EndRequestHandler", "function EndRequestHandler(sender,args){ParseScript($get('" + updatePanel.ClientID + "_Container'));};\r\n", true);

Not that the ParseScript function traverses the controls in the page and find childern with the tag name of <SCRIPT>, so I wanted to start with a UpdatePanel parent, and since the id is dynamically generated, I need to add the EndRequestHandler client side javascript from the server.

3) Next write ParseScript to traverse the childern and move all SCRIPT tags to the head, I will let you write your own code, cause this is QED.

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

ASP.NET | Wayne | Ajax
Friday, August 15, 2008 9:20:08 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, August 14, 2008

I took off 4 months to remodel my kitchen, in that time I did no programming.  Now I am back to work for at least a week now.  I havn't forgotten anything (except for the SQL Server sa password -- which means that it is a good one.)  I can program just as well as before I left. 

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

Thursday, August 14, 2008 9:06:09 AM (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]  | 
 Friday, February 15, 2008

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

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]  | 
 Thursday, February 07, 2008

In response to the previous postingJames Arendt has comment the final word:

static internal String AredntWay(String[] list)
{
   return(String.Join(',',list));
}

Thanks James Arendt!

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

C# | Wayne
Thursday, February 07, 2008 2:21:47 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  | 

C# .NET 3.0 has anonymous types, since I am working on some legacy projects I have the experience of using anonymous types already in VBScript.  Remember classic ASP? Not having to Dim your variables, and being able to assign a variable to an int, then a string.  Has it been so long ago that you forgotten all the fun you have had with anonymous types in classic ASP?

I have a project that is classic ASP and SQL Server, if we change the SQL Server column (i.e. rename it or change the type) we have to scan the classic ASP (Find In Files) then make the changes to all inline SELECTS, UPDATES, and DELETES.

So then we got smart and changed to stored procedures, now if the type changes the stored procedures doesn't compile, easier to find places that need to be changed, however we had to adjust the classic ASP to handle the new type as an output -- really not a problem becuase all VBScript is anonymous types. 

So then we got really smart and used ASP.NET CLR 2.0, a class library to talk to our database, and SQL Server.  We generated the class library from CodeSmith using the database schema as a template for the library.  Now we are doing really good.  If the database type changes, the class library changes (we regenerate it), and any code behind that is calling the class library will break becuase of the type change.  Strongly typed language how nice.

Now we have LINQ -- back to anonymous types, we can jump on the bandwagen and rewrite all our code behind to call LINQ.  Now if we make a database change we are back to find In files.  Full circle to our VBScript days. Yippee.

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

 

C# | Wayne
Thursday, February 07, 2008 1:46:36 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, February 04, 2008

Creating a comma delimited string from an array came up yesterday, here is a couple of methods, one from our junior developer and one that I did.  I entitled mine old school, because of the C coding that shaped my programming.

static internal String Junior(String[] list)
{
    String output = String.Empty;

    foreach (String item in list)
    {
        output += item + ",";
    }

    output.TrimEnd(',');

    return (output);
}

static internal String OldSchool(String[] list)
{
    StringBuilder output = new StringBuilder();

    foreach (String item in list)
    {
        if (String.IsNullOrEmpty(item))
            continue;

        if (output.Length > 0)
            output.Append(",");

        output.Append(item);
    }

    return (output.ToString());
}

Which one do you like?

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

 

 

C# | Wayne
Monday, February 04, 2008 10:21:22 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  | 
 Friday, February 01, 2008

I have several reasons not to use open source and I will be posting all in short order.  By open source, I mean a free assembly I can get on the web that includes the source, usually a development tool that is build by a group of people.

For some time I have been debating weather to use open source projects for my C# programming, or to buy professional components.  By debating, I mean thinking about, trying, and experimenting with both -- however now it is time to put my ideas to paper and share.  Disclaimer: in a year I might change my mind, or a posted comment might change my mind.  However here is one tought to chew on:

We reference assemblies so that we can gain functionality without having to code the funtionality ourselves.  Microsoft provides some great functionality in the CLR and it allows us to make significant gains in productivitiy by not making up write the stuff in the CLR.  However, if Microsoft is missing the functionality and it isn't specific to your vertical or product -- there is a tendency to get the functionality from the web as a open source project.  I tend to get the source, recompile it and make it a project within my solution, instead of using the assembly already compiled -- this can be a whole other discussion.  Ok, to recap: I reference an assembly from the Internet because I don't want to write the code myself.

However, it is my experience that the open source projects are very buggy, which leaves me fixing them to get them to work.  Which means I am coding in someone elses code just to get the thing to work correctly.  If I purchased a professional product, I could just write customer support, provide a reproduction, and get a fix.  Which leads me to reason #1: There is no support, I would rather buy (and I mean have my company buy) a $1000 per license product and get support for bugs then fix and worry about the open source.  

Just to drive the point home, even popular projects like ICSharpCode.SharpZLib have bugs, I just fixed three of them.  And, no I am not going to submit the changes to the open source project -- it takes to much time, they will not like my changes (since I patched it and didn't fix it like I would my own code), and usually those developers are hard to reach (hiding from doing customer service) and snobby (not SharpZLib in paricular just open source developer in genderal).

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

 

Friday, February 01, 2008 8:54:09 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  | 
 Tuesday, January 29, 2008

Charter Communications officials believe a software error during routine maintenance caused the company to delete the contents of 14,000 customer e-mail accounts.

http://www.foxnews.com/story/0,2933,325338,00.html

I am glad today that I didn't do this and force my company to give a $50 credit to each customer.  Ouch.

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

Tuesday, January 29, 2008 9:11:44 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Friday, January 25, 2008

Will be presenting at the Seattle Code Camp https://seattle.codecamp.us/ this Saturday.  My talk is entitled "Kick Your Hash" and I will attempt to bridge the cryptography gap between code and theory by showing SQL Server and .NET code, real life examples, and practical correct uses for Hashing.

Example 1:

SELECT HashBytes('MD5','password')

SELECT HashBytes('MD5',CONVERT(varchar(max),'password'))

SELECT HashBytes('MD5',CONVERT(nvarchar(max),'password'))

Example 2a:

ALTER PROC CheckLogin
@Login varchar(50),
@Password varchar(50),
@Valid bit OUTPUT
AS

SET NOCOUNT ON

SELECT *
FROM [User]
WHERE @Login = [User].[Login] AND [User].Hash = 
    HashBytes('MD5',CONVERT(varchar(max),[User].Prefix) + @Password)

IF (@@ROWCOUNT>0)
    SET @Valid = 1
ELSE 
    SET @Valid = 0

Example 2b:

CREATE TABLE [dbo].[User](
    [UserId] [uniqueidentifier] NOT NULL CONSTRAINT [DF_User_UserId]  DEFAULT (newid()),
    [Login] [varchar](50) COLLATE SQL_Latin1_General_CP1_CI_AS NOT NULL,
    [Hash] [varbinary](16) NOT NULL,
    [Prefix] [uniqueidentifier] NOT NULL,
 CONSTRAINT [PK_User] PRIMARY KEY CLUSTERED 
(
    [UserId] ASC
)WITH (PAD_INDEX  = OFF, IGNORE_DUP_KEY = OFF) ON [PRIMARY]
) ON [PRIMARY]

Example 2c:

DECLARE @Prefix uniqueidentifier
SET @Prefix = NewId()

INSERT INTO [User]
(
    [Login],
    Hash,
    Prefix )
VALUES(
    'wayne',
    HashBytes('MD5',CONVERT(varchar(max),@Prefix) + 'password'),
    @Prefix)

Example 3:

CREATE PROC ChangePassword
@Login varchar(50),
@OldPassword varchar(50),
@NewPassword varchar(50)
AS

UPDATE [User]
SET Hash = HashBytes('MD5',CONVERT(varchar(max),[User].Prefix) + @NewPassword)
WHERE @Login = [User].[Login] AND [User].Hash = 
    HashBytes('MD5',CONVERT(varchar(max),[User].Prefix) + @OldPassword)

Example 4a:

CREATE PROC AddData
@Data varbinary(max)
AS

INSERT INTO Data
(
    Data,
    Hash,
    [Size]
)
VALUES
(
    @Data,
    HashBytes('MD5',@Data),
    DATALENGTH(@Data)
)

Example 4b:

CREATE PROC FindData
@Data varbinary(max),
@Id uniqueidentifier OUTPUT
AS

DECLARE @Hash varbinary(16)
SET @Hash = HashBytes('MD5',@Data)

DECLARE @Length bigint
SET @Length = DATALENGTH(@Data)

SELECT @Id = DataId
FROM Data
WHERE @Hash = Hash AND @Length = Size
{6230289B-5BEE-409e-932A-2F01FA407A92}
 
T-SQL | Wayne
Friday, January 25, 2008 11:28:56 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, January 24, 2008

I have been adding {6230289B-5BEE-409e-932A-2F01FA407A92} at the bottom of my posts.  What posts you ask?  Well every post I am doing on the internet.  MSDN Managed Newsgroup forms, woodworking forums, and this blog are just some of the places.  Why you ask?  Well I want to be able to find all my posts that are unique to me.  It just so happens I share my name with several other people on the Internet so I wanted a unique name that was anonymous that would find only my stuff.  I will be adding to my BIOs when I write articles, and my web sites.  This way I can go to Google and search for my GUID and find everthing on the Internet that I have done.  You can do the same, just generate yourself a GUID at: http://www.guidgen.com/ and get started.

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

 

Thursday, January 24, 2008 9:20:01 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  | 
 Tuesday, January 22, 2008

In C# we can have nullable types, like this:

Boolean? isValid;

Which means that isValid can be true, null, or false.  It is like a three way switch.  I use it to represent a bit field in SQL Server that can have a null value.  For example I might have this table:

CREATE TABLE Account (IsValid bit)

Which creates me a table with a bit column that can be NULL.  I usually don't like to do this -- in fact I never do, however I am working with some people that don't bother to check the NOT NULL when they use the designer so I have to deal with it in my C# code.  So I ask the table designer what NULL means in the IsValid coulmn and he tells me it means false (not valid).

So I query the database and set the result to isValid, in the C# class above.  So now I need to check to see if the account is valid.  It might seem like this would work:

if (isValid) {...}

However, that throws a compiler error, becuase you can't have Nullable type default expression.  So I have to do this:

if (isValid == true) { }

Notice that the second statement is really saying that the expresion fails if the IsValid is false or null.  This is also the same but weak:

If ((isValid!=null) && (((Boolean)isValid)==true)) { }

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

 

C# | Wayne
Tuesday, January 22, 2008 9:18:07 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  | 
 Monday, January 21, 2008

"When you program by yourself do you use source control?"

What I want to hear is that they use source control.  Source control -- even by yourself -- is an important tool that helps you backtrack out of coding tangents that fail.  Hosted on another server it is a valuable form of backup off your own box.  It also shows that the person has programmed in a shop where source control was enforced (rightfully so) and understand how it beneficial it is.

 

Monday, January 21, 2008 7:28:36 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [2]  | 
 Friday, January 18, 2008

Another programming tip in the article I read was to keep a task list for the work left in your programming project and work off it.  Most projects are so large that you can't keep track of all the loose ends.  I have never had trouble keeping track of the loose ends of a project when my head is in it -- however I know the day is coming soon.

Being a former Microsoft employee and Microsoft being an email culture (at least when I was there in 1997), I use my Inbox to keep track of all my tasks.  Ten years ago it was around 200 emails -- all that needed action.   I was alright with that since I got 100-400 emails a day I was keeping them under 200 and everything was good.  About 5 years ago the inbox topped 300 emails and I was still alright with that -- keeping them paired down and knowing I would never clear it completely -- remember all 300 required action.  At the end of 2007 I had 400 emails in the inbox and I knew there was a problem -- so I set about clearing my inbox.  I have only cleared my inbox once in the past.

I just got done today.  It took me 40 hours to clear all the emails, about 10 emails and hour.  40 hours performing the email task, replying or just making tough decisions and deleting them. 

My typical daily process: my inbox usually has newest at the top and everyday I work from the top down.  Most of the time I reach the last email I didn't take action on yesterday and then work ten or so farther down.  However most of those require a reply from someone and I get discourage.  On Monday I try to follow-up or work all of last weeks emails and all of Mondays. 

So here is my tip list for clearing your inbox:

  1. Start at the oldest email and work up.  The newer emails are ones that you are waiting for a reply.  The oldest ones (in my case a year old) are ones you are never getting a reply on, i.e. easier to delete.
  2. If you have a customer question you didn't answer within a month -- they are no longer a customer, delete the email.
  3. Delete all the emails forward by your mother-in-law which you didn't think of something witty to reply back.  Following up after 6-months will not earn you any points.
  4. Sort by subject -- sometimes there is a ten email thread that you already handled which you can delete.  It also allows you to easily scan for the same types of emails which you missed deleting the first time they came by.  It also breaks the reply mental block
  5. Delete the email you sent to yourself with articles you wanted to read -- you just don't have time obviously.
  6. If you really can't decide to delete it -- leave it for the end -- the more you do this the more you want to delete it.
  7. Finish the small tasks you meant to do however put off -- they are small will not take you long, and you can delete that task email.
  8. It is never to late to reply to a friend and go to lunch.  Reply with dates that work and delete -- you don't have to handle that task until they write back.
  9. Make well named folder and move the email.  If it is a reference article, customer feedback, or FAX you need to save get it organized.

Good Luck

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

 

 

Friday, January 18, 2008 8:02:40 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Thursday, January 17, 2008

Intermitently my code was getting this Exception: "System.ArgumentException: An item with the same key has already been added."  The code looked like this:

if (!_roleTypeNameDict.ContainsKey(roleTypeName))
_roleTypeNameDict.Add(roleTypeName, ...)

Since I have done a lot of C++ programming right away I realized that I had a threading issue, however the question was why? It just so happens this was a static method, calling a static property, which means that more then one thread might be accessing _roleTypeNameDict at a time:

private static Dictionary<String, RoleTypes> _roleTypeNameDict
   = new Dictionary<string, RoleTypes>();

public static RoleTypes FindCachedRoleTypesFromRoleTypeName(String roleTypeName)
{

if (!_roleTypeNameDict.ContainsKey(roleTypeName))
       _roleTypeNameDict.Add(roleTypeName, ...);
...
}

To solve the issue you need to create a lock to protect the check to add from the add like this:

private static Dictionary<String, RoleTypes> _roleTypeNameDict
   = new Dictionary<string, RoleTypes>();
private
static object _roleTypeNameDictLock = new object(); public static RoleTypes FindCachedRoleTypesFromRoleTypeName(String roleTypeName) {    lock (_roleTypeNameDictLock) {    if (!_roleTypeNameDict.ContainsKey(roleTypeName))       _roleTypeNameDict.Add(roleTypeName, ...); }
   ...
}

However, you can make the code slightly faster if you assume that the ContainKey is less expense then the lock from a performance stand point:

private static Dictionary<String, RoleTypes> _roleTypeNameDict
   = new Dictionary<string, RoleTypes>();
private static object _roleTypeNameDictLock = new object();

public static RoleTypes FindCachedRoleTypesFromRoleTypeName(String roleTypeName)
{
   if (!_roleTypeNameDict.ContainsKey(roleTypeName))
   {
      lock (_roleTypeNameDictLock)
      {
         if (!_roleTypeNameDict.ContainsKey(roleTypeName))
            _roleTypeNameDict.Add(roleTypeName, ...);
      }
   }
   ...
}

What we are doing here is checking to see if we need to add, then locking, then checking again, then adding.  This prevents us from locking if we don't need to add-- the majoirty of the cases, however it also prevents two threads from trying to add at the same time.

I was just thinking the other day that threading, caching, and memory management skills where still needed in .NET even though the Microsoft PR machines is making C# out to be much easier then C++.  At the same time VBScript programmers in classic ASP don't have to worry about threading issues.

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

 
 
C# | Wayne
Thursday, January 17, 2008 8:53:30 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Wednesday, January 16, 2008

Let's say you forgot a WHERE clause and want to restore your backup database to recover the lost data.  However, you don't want to override the data that has accumulated since the last back-up.  What you need to do is restore the backup along side the newer database.  Here is how to do it:

RESTORE DATABASE backup
   FROM 'C:\temp\lastbackup.bak'
   WITH MOVE 'mydb_Data' TO 'C:\MySQLServer\backupdb.mdf',
   MOVE 'mydb_Log' TO 'C:\MySQLServer\backupdb.ldf';

This statement tells the SQL server to create a database called "backup", from the lastbackup.bak file and not to use the files stored in the back-up, but instead use: backupdb.mdf and backupdb.ldf.  Unless you remap your backup files, the RESTORE will try to override the old files that are in use by the newer database -- which will cause a SQL Server error (not data loss).


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

Wednesday, January 16, 2008 7:43:40 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Tuesday, January 15, 2008

Detaching a database in SQL Server doesn't delete the files (.ldf, .mdf, .ndf) where the data is stored.  Deleting a database is different then detaching a database.  Detaching allows you to move the files and reattach them to a different SQL Server.  However, once you detach a database it is not available to query.

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

Tuesday, January 15, 2008 7:37:18 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, January 14, 2008

My brother called, he had forgotten a WHERE clause and had lost some data.  I felt really bad for him -- the kind of sinking feeling you get when someone you know has a death in the family.  I can relate -- I have forgotten a few WHERE clauses in the past couple of years.  Right after he called I checked my database backups to make sure they where still running every night.

This reminded me of one of my favorite interview questions: "Tell me about the last time you forgot a WHERE clause?"  I am looking for that sinking recognition that takes place when someone knows the question and can relate.  And if they have never forgotten a WHERE clause -- I just know they haven't programmed much T-SQL, cause we all do it from time to time.  I also want to hear how they recovered their data and what they are doing differently to prevent the problem.  A developer I know at work always writes the WHERE clause before he writes the DELETE or UPDATE line just to prevent the aforementioned.

Maybe SQL Managem