Monday, March 31, 2008

I use SourceGear Vault and generally love it. Guessing the following is not directly related to Vault but rather source control in general. By default, Vault is configured to ignore the Bin folder within an ASP.NET project or Web Site.

What have I been doing for years when I need to import a vendor's DLL to my project? Yup, I copy it to my Bin folder and add a reference to it. Wrong thing to do!

That 3rd party DLL is not achieved along with the rest of my project since Vault is configured to ignore the Bin folder. (Guessing that all source control systems follow this behavior.) What should be doing instead? Create a folder within my project along the lines of AcmeLibrary and copy DLLs there. Then reference my DLLs and Visual Studio will copy them to your Bin folder when you build your project. The AcmeLibrary and its contents will be placed under source control.

Hope this saves you some future grief!

kick it on DotNetKicks.com   Monday, March 31, 2008 6:53:51 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [2]  | 
 Friday, March 07, 2008

I find myself repeating the same little code pattern time after time when building a LINQ expression based on a number of optional filtering criteria. Using standard if statements seems a bit verbose to me.

if (SearchControlMain.CategoryID.HasValue)
    query = query.Where(q => q.CategoryID == SearchControlMain.CategoryID);

if (SearchControlMain.TypeID.HasValue)
    query = query.Where(q => q.TypeID == SearchControlMain.TypeID);

if (SearchControlMain.PostingID.HasValue)
    query = query.Where(q => q.PostingID == SearchControlMain.PostingID);


Completely inlining the if statements doesn't do much for readability.

if (SearchControlMain.CategoryID.HasValue) query = query.Where(q => q.CategoryID == SearchControlMain.CategoryID);
if (SearchControlMain.TypeID.HasValue) query = query.Where(q => q.TypeID == SearchControlMain.TypeID);
if (SearchControlMain.PostingID.HasValue) query = query.Where(q => q.PostingID == SearchControlMain.PostingID);


So I created a couple of quick extension methods on both the IEnumerable and IQueryable forms of Where. I named my new extension WhereIf but I struggled a bit as to whether to name it just Where and wind up with an overload.

Now you can code:

query = query.WhereIf(SearchControlMain.CategoryID.HasValue, q => q.CategoryID == SearchControlMain.CategoryID);
query = query.WhereIf(SearchControlMain.TypeID.HasValue, q => q.TypeID == SearchControlMain.TypeID);
query = query.WhereIf(SearchControlMain.PostingID.HasValue, q => q.PostingID == SearchControlMain.PostingID);


Here are the extension methods:

public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, bool condition, Func<TSource, bool> predicate)
{
    if (condition)
        return source.Where(predicate);
    else
        return source;
}

public static IEnumerable<TSource> WhereIf<TSource>(this IEnumerable<TSource> source, bool condition, Func<TSource, int, bool> predicate)
{
    if (condition)
        return source.Where(predicate);
    else
        return source;
}

public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Expression<Func<TSource, bool>> predicate)
{
    if (condition)
        return source.Where(predicate);
    else
        return source;
}

public static IQueryable<TSource> WhereIf<TSource>(this IQueryable<TSource> source, bool condition, Expression<Func<TSource, int, bool>> predicate)
{
    if (condition)
        return source.Where(predicate);
    else
        return source;
}

kick it on DotNetKicks.com   Friday, March 07, 2008 8:29:01 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  | 
 Monday, February 25, 2008

An update on an older posting now that we have LINQ:

 

string[] input = { "1", "2", "3", "4", "5", "6", "7", "8", "9" };

 

// old

int[] output = Array.ConvertAll<string, int>(input, delegate(string s) { return int.Parse(s); });

 

// new

int[] output = Array.ConvertAll(input, s => int.Parse(s));

 

// even better

input.Select(s => int.Parse(s)).ToArray();

 

// better yet

input.Select(s => int.Parse(s)).ToList();

 

kick it on DotNetKicks.com   Monday, February 25, 2008 4:19:12 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Sunday, February 24, 2008

When you use the LINQ to SQL design surface in Visual Studio, the generated code contains a Column Attribute which in turn contains a small piece of SQL text called the DbType. This property contains the column length for columns of type Char and VarChar. Using a bit of reflection, you can gain access to the value. Wrapping all of this within an ExpressionBuilder, you can easily set the MaxLength property of any TextBox declaratively in your web pages.

Here is the code for the Expression Builder:

using System;

using System.CodeDom;

using System.Data.Linq.Mapping;

using System.Linq;

using System.Reflection;

using System.Web.Compilation;

using System.Web.UI;

 

namespace BinaryOcean.Web.Utility

{

    [ExpressionPrefix("LinqLength")]

    public class LinqLengthExpressionBuilder : ExpressionBuilder

    {

        public override CodeExpression GetCodeExpression(BoundPropertyEntry entry, object parsedData, ExpressionBuilderContext context)

        {

            string typeName = BeforeLast(entry.Expression, ".");

            string propertyName = AfterLast(entry.Expression, ".");

 

            return new CodePrimitiveExpression(PropertyLength(typeName, propertyName));

        }

 

        private int PropertyLength(string typeName, string propertyName)

        {

            ColumnAttribute attribute =

                (ColumnAttribute)BuildManager

                .GetType(typeName, true)

                .GetProperty(propertyName)

                .GetCustomAttributes(typeof(ColumnAttribute), false)

                .Single(); // <-- look, I just had to use a tiny bit of LINQ in this code!

 

            return int.Parse(BetweenFirst(attribute.DbType, "char(", ")"));

        }

 

        private string BeforeLast(string value, string last)

        {

            return value.Substring(0, value.LastIndexOf(last));

        }

 

        private string AfterLast(string value, string last)

        {

            return value.Substring(value.LastIndexOf(last) + last.Length);

        }

 

        private string BetweenFirst(string value, string startText, string endText)

        {

            int start = value.IndexOf(startText, StringComparison.OrdinalIgnoreCase) + startText.Length;

            int end = value.IndexOf(endText, start,  StringComparison.OrdinalIgnoreCase);

 

            return value.Substring(start, end - start);

        }

    }

}

You must add the following bit of code to the system.web / compilation section of your web.config file. This loads the ExpressionBuilder which interprets your code.

 

<system.web>

  <compilation debug="true">

    <expressionBuilders>

      <add expressionPrefix="LinqLength" type="BinaryOcean.Web.Utility.LinqLengthExpressionBuilder"/>

    </expressionBuilders>

  </compilation>

</system.web>

 

The use of BuildManager.GetType() within the Expression Builder should allow you to reference dbml objects within you local AppCode folder or either local or external namespaces. Given that Name is a database column contained within the Employee table you can code this:

 

<asp:TextBox ID="TextBoxName" runat="server" MaxLength='<%$LinqLength:Employee.Name%>' />

 

If you are using Web Projects or if your data context is in a different namespace from your web application, you will have to prefix the above type with your namespace (and add dot ("."));

 

Enjoy!

kick it on DotNetKicks.com   Sunday, February 24, 2008 3:43:19 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Monday, January 28, 2008

Love my new Zune, but I wasn't able to install the software. Received the following error:

Installation Failed
Setup must stop because the required package ‘Zune’ failed to install.
DIFXAPP: Rollback failed with error 0x2
Error code: 0x80070643
http://go.microsoft.com/fwlink/?LinkID=101253

Looking at the support boards, it seems that a number of people are having this issue. I did find a reference to an obscure registry hack that disabled the installer's ability to rollback after a failed install. This seemed to fix the issue but I tried upgrading to the new 2.3 version of the software which recently became available. No go. Infact, the upgrade process failed and broke my previously working software.

Yuck! I once again followed the link in the above error message. It was of zero help. On a lark, I tried re-enabling the Firewall Service that I had long ago disabled. Fixed! Seems that you can turn your firewall off, but can't disable the service. Guessing the install was trying to configure ports on the firewall.

Zune: Great product. Zune client software: Poor QA.

kick it on DotNetKicks.com   Monday, January 28, 2008 2:20:20 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [1]  | 
 Friday, September 28, 2007

I recently needed a dictionary for a small project. Nothing special there but on this occasion, I needed a dictionary that was keyed by two sub-keys. I thought about using an array but you loose strong typing. I thought about using  a dictionary of dictionaries but the code is messy, complicated and difficult to maintain. Here is a better solution. (I also coded structures that allow for compound keys with 3 and 4 values. If you need that, just expand on the 2 key version.):

 

[Serializable, StructLayout(LayoutKind.Sequential)]

public struct CompoundKey<T1, T2>

{

    private T1 key1;

    private T2 key2;

 

    public CompoundKey(T1 key1, T2 key2)

    {

        this.key1 = key1;

        this.key2 = key2;

    }

 

    public T1 Key1

    {

        get { return this.key1; }

    }

 

    public T2 Key2

    {

        get { return this.key2; }

    }

 

    public override string ToString()

    {

        StringBuilder builder = new StringBuilder();

        builder.Append('[');

 

        if (this.Key1 != null)

        {

            builder.Append(this.Key1.ToString());

        }

 

        builder.Append(", ");

 

        if (this.Key2 != null)

        {

            builder.Append(this.Key2.ToString());

        }

 

        builder.Append(']');

        return builder.ToString();

    }

}

 

My dictionary:

 

Dictionary<CompoundKey<int, string>, string> dictionary = new Dictionary<CompoundKey<int, string>, string>();

 

dictionary.Add(new CompoundKey<int, string>(1, "A"), "X123");

dictionary.Add(new CompoundKey<int, string>(2, "B"), "M394");

dictionary.Add(new CompoundKey<int, string>(3, "B"), "M395");

 

if (dictionary.ContainsKey(new CompoundKey<int, string>(3, "B")))

{

    Fred = dictionary[new CompoundKey<int, string>(3, "B")];

}

kick it on DotNetKicks.com   Friday, September 28, 2007 6:43:04 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  | 
 Saturday, August 25, 2007
ASP.NET Tip: Occasionally I need to establish a line of communication between a User Control and my Master Page. Over the years, I have explored a few methods to accomplish this and seen more than a handful from others. The following method elegantly solves a number of problems for me and has become my standard coding practice.

First, a base class is needed for all Master Pages:

using System;

 

public abstract class BaseMaster : System.Web.UI.MasterPage

{

    public BaseMaster() { }

 

    public abstract void ShowMenu(bool visible);

    public abstract void SetStatusMessage(string message);

}

I define this class as abstract since it will never be instantiated directly. In addition, an abstract class allows us to include abstract methods and properties which in turn requires us to provides these methods on our actual Master Page.

My actual master page is coded as follows:

using System;

 

public partial class Main : BaseMaster

{

    protected void Page_Load(object sender, EventArgs e) { }

 

    public override void ShowMenu(bool visible)

    {

        MenuMain.Visible = visible;

    }

 

    public override void SetStatusMessage(string message)

    {

        LabelStatus.Text = message;

    }

}

Within a User Control, you can gain access to the Master Page via the Page.Master object. Using our base class (BaseMaster) you get a strongly typed object. You migh be tempted to cast the Page.Master object to the type of your Master Page but this limits you to a single Master Page. The base class method allows your site to implement multiple master pages and still access these common methods and properties.

using System;

 

public partial class WebUserControl : System.Web.UI.UserControl

{

    protected void Page_Load(object sender, EventArgs e) { }

 

    protected void ButtonUpdate_Click(object sender, EventArgs e)

    {

        BaseMaster master = Page.Master as BaseMaster;

 

        if (master == null)

            return;

 

        master.SetStatusMessage("Update Complete");

    }

}

One final consideration: your User Control should likely also derive from some type of base class. Within this base class provide matching properties and methods to those in your BaseMaster. This will simplify things for you by eliminating the need to recode the same methods and properties in each of your User Controls. If you are acessing these same methods from your Page, consider including methods in a page base class.

kick it on DotNetKicks.com   Saturday, August 25, 2007 8:28:37 PM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [2]  | 
 Tuesday, August 14, 2007

I see this a lot:

public int CompanyID

{

    get

    {

        if (ViewState["CompanyID"] == null)

        {

            ViewState["CompanyID "] = 0;

        }

        return int.Parse(ViewState["CompanyID "].ToString());

    }

    set { ViewState["CompanyID "] = value; }

}


What is wrong with it? 2 things:

1.       ViewState stores and returns an object so the get{} portion of the property that first converts this to a string and then parses it back to an integer is rather inefficient.

2.       This particular property is wrapping a value type (int) and as such, there is no need to persist the default value in  ViewState. This unnecessarily adds to the size of ViewState on a page. A better approach would be to return the default value (or any value you choose) when the ViewState object is null.

So, a better solution:

public int CompanyID

{

    get { return (int)(ViewState["CompanyID"] ?? 0); }

    set { ViewState["CompanyID"] = value; }

}
 

Remember that the C# coalescing operator (??) returns the left side value if said value is non-null and the right side value when the left side is null. We don't create an entry in ViewState until we assign a non-nulll value.

One thing to watch out for when using this pattern is when storing certain reference types in the ViewState object.

private Dictionary<Guid, bool> ToAttach

{

    get

    {

        if (ViewState["ToAttach"] == null)

        {

            ViewState["ToAttach "] = new Dictionary<Guid, bool>();

        }

        return (Dictionary<Guid, bool>)ViewState["ToAttach "];

    }

    set { ViewState["ToAttach "] = value; }

}

Testing for null and assigning an empty dictionary in the get{} portion of the property insures that we always return the same object once it is newed up. If we didn’t perform this test and tried using the coalescing operator as we did with the value type above, we would return a different object each time the property is reference and any code attempting to manipulate the object without explicitly setting the value would fail.

kick it on DotNetKicks.com   Tuesday, August 14, 2007 10:08:49 AM (Pacific Standard Time, UTC-08:00)  #    Disclaimer  |  Comments [0]  |