Posts in category .net - page 30

Automatic comparison operator overloading in C#

Abhinaba has blogged on the painful C# operator overloading experience.

The basic problem that is if you want to overload one, you soon end up many of the tests including == != < <= > >= Equals, Compare and probably CompareTo via IComparable when it turns out one function can provide everything needed in general use.

GitHub has the latest version of AutoOperators

Ruby already has this with the <=> operator, sometimes known as a SpaceShipOperator and this got me thinking resulting in the following little class:

using System;

/// AutoOperators provides a base class where all the standard operators are overridden and
/// wired up via the subclass's implementation of IComparable.CompareTo.
public abstract class AutoOperators : IComparable {
  public static bool operator < (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) < 0;
  }
  public static bool operator > (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) > 0;
  }
  public static bool operator == (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) == 0;
  }
  public static bool operator != (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) != 0;
  }
  public static bool operator <= (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) <= 0;
  }
  public static bool operator >= (AutoOperators obj1, AutoOperators obj2) {
    return Compare(obj1, obj2) >= 0;
  }

  public static int Compare(AutoOperators obj1, AutoOperators obj2) {
    if (Object.ReferenceEquals(obj1, obj2)) return 0;
    if ((object)obj1 == null) return -1;
    if ((object)obj2 == null) return 1;
    return obj1.CompareTo(obj2);
  }

  public abstract int CompareTo(object obj);
  public abstract override int GetHashCode();

  public override bool Equals(object obj) {
    if (!(obj is AutoOperators)) return false;
    return this == (AutoOperators) obj;
  }
}

And then to use it simply inherit from it and implement GetHashCode and CompareTo, e.g.

using System;

public class SampleClass : AutoOperators {
  private int testValue = 0;

  public SampleClass(int initialTestValue) {
    testValue = initialTestValue;
  }

  public int TestValue {
    get { return testValue; }
    set { testValue = value; }
  }

  public override int CompareTo(object obj) {
    if (obj is SampleClass)
       return TestValue.CompareTo(((SampleClass) obj).TestValue);
    else
       return -1;
  }

  public override int GetHashCode() {
    return TestValue.GetHashCode();
  }
}

As with all code, determine the suitability of this solution to your own needs – that responsibility is yours. As with all code here it comes without warranty, expressed, implied or otherwise alluded to.

I’d recommend checking out the performance, CLS compliance regarding operators and the addition of any extra operators you use all the time.

[)amien

Conditional operator bug in .NET 1.x & 2.0

Update: This behavior is as expected according to the .NET team but it’s probably unexpected for most users.

I encountered a strange problem this week when a conditional operator appeared to be evaluating the false expression contrary to the C# documentation. The line looked like:

return testObject == null ? null : testObject.InstanceVariable;

The point of this line is to prevent accessing .InstanceVariable if the object is null and yet every time this line executed a NullReferenceException is thrown (and no I wasn’t overloading the == operator).

With a little experimentation I was able to narrow it down and produce a simple test case that exercised the bug on .NET 1.1 and .NET 2.0.

The problem is this: If the return parts are not of the same type and one of the members supports implicit conversion to the expect type, then it is called regardless of whether it is the the true part or not.

I filed the bug with Microsoft and it was confirmed last night by one of their C# engineers, there is a small chance it will be fixed in .NET 2.0 before release but we’ll have to see. I checked out some of the .NET classes and they too always access the object during an implicit conversion, however they are all value types which can not be null.

In the mean time there is an easy way to avoid this by modifying your implicit conversion methods to check for null before converting.

public static implicit operator string(ClassWithImplicitConversion objToConvert) {
    return objToConvert == null ? string.Empty : objToConvert.ToString();
}

A full test case appears below:

using System;

namespace ProveConditionalBug {
     class Program {
         static void Main(string[] args) {
             Console.Out.WriteLine("Testing... " + ProveConditional());
         }

        static string ProveConditional() {
            ClassHoldingImplicitConversionMember testObject = null;
            return (testObject == null) ? null : testObject.InstanceVariable;
        }
     }

    class ClassHoldingImplicitConversionMember {
        public ClassWithImplicitConversion InstanceVariable = null;<br />     }

    class ClassWithImplicitConversion {
         public string Value = "Test";
         public static implicit operator string(ClassWithImplicitConversion objToConvert) {
             return objToConvert.Value;
         }
     }
}

[)amien

Avoiding SQL injection

Back in ’98 I was developing an extranet site for a local company when I realized that it would be open for exploit if somebody put single quotes in text fields. It was early in the development cycle so I fixed it and moved on, unable to find out how other people were avoiding the problem.

It turned out many were not and it became a well-known exploit called SQL injection. Unfortunately there are many developers who don’t know or appreciate the problem, and it is this:

If you build SQL by appending strings and data without correct encoding your application can be exploited. These exploits can range from exposing sensitive information, through to modification and deletion of data.

This problem is very real and applies to:

  • All SQL statements, not just SELECT
  • All database systems, not just MS SQL or MySQL
  • All programming environments, not just C#, PHP or ASP
  • All data, most essentially that obtained from end-users, regardless of client-side checking

Let’s walk through an example and see how it works and what can be done to avoid it.

Example: User login

We have a user-name and password from a web form and want to get the users ID from the database, or nothing if it wasn’t valid. We want to send something like this SQL statement to our database.

SELECT UserID FROM Users WHERE UserName='Bob' AND Password='test'

And so a developer might do something like this (in C# using .NET);

var dr = connection.Execute("SELECT UserID FROM Users WHERE UserName='" + Request("UserName") + "' AND Password='" + Request("Password") + "'");
if (dr.Read()) userId = dr.GetInt32(dr.GetOrdinal("UserID"));

The problem here is that if there is a ‘ in the form fields it effectively breaks out of the selection criteria and allows the end user to add extra criteria or even commands to what we are sending to the database server. Should they enters the following into the password form field…

    ' OR ''='

Then our code above will send the following SQL to the database:

SELECT UserID FROM Users WHERE UserName='aaa' AND Password='' OR ''=''

Which will return every record in the database and our code will let him log in as the first user it finds – normally a developer or administrator account. Ouch!

Bad solution: Encode it yourself

One solution often adopted is to always ensure all string input has a single-quote replaced by two single-quotes, which is what SQL server expects if you really want to send it a single quote.

This solution fails in that it doesn’t handle numbers or dates and falls apart in that both numbers and dates are often regionally formatted.

Good solution: Let the DB client encode it

A much better solution is to use your environment to perform all the proper encoding for you. As well as protecting you from such exploits you’ll also avoid localization problems where the string representation of something on your client is interpreted differently in your database. This can be a real problem in the UK where dates formatted by a UK web-server are sent to a misconfigured SQL server expecting US formatting and the days and months become transposed without error.

var cmd = new SqlCommand("SELECT UserID FROM Users WHERE UserName=@UserName AND Password=@Password");
cmd.Parameters.Add(new SqlParameter("@UserName", System.Data.SqlDbType.NVarChar, 255, Request("UserName")))
cmd.Parameters.Add(new SqlParameter("@Password", System.Data.SqlDbType.NVarChar, 255, Request("Password")))
dr = cmd.ExecuteReader();
if (dr.Read()) userId = dr.GetInt32(dr.GetOrdinal("UserID"));

Okay there is no doubt this is a little longer but it takes care of all our encoding and localization worries, and if for example you want to insert a lot of data into the database, creating the command and parameters once, then just setting the parameters for each insert (and executing it) will run faster than lots of string building inserts…

Moral of the story

Always encode data properly. If you can use the provided methods and functions to do so. If none are provided grab the specification and find out all the special characters used. Learn what encoding and escape sequences are used and apply them properly.

A few places where data should be encoded properly:

  • HTML – Obviously < and > have special meanings and need to be escaped. ASP.NET controls will take care of this if you set the .Text or .InnerText properties but set .InnerHTML at your own peril. Old ASP has the Server.HTMLEncode() function.
  • URL – A whole host of rules but the query string is often modified in code. Use URLEncode() or something similar especially if you want XHTML compliance too.
  • XML – Again a whole host of rules for what is valid data. Either use an XML object to write out your data (MSXML, Xerces etc) or maybe even store it in [[CDATA sections.
  • CSV – Even comma-separated value files have encoding rules. What do you need to do if text is going to have a ” in a field. What happens if a number contains a comma! Find out or use a well regarded library to do it for you.

Notes about the example

A better login system would not allow the web server direct access to sensitive data such as the user table. All access to sensitive information should be through stored procs that enforce those restrictions.

Such a login system would therefore call a stored procedure that logged the attempted, decided if it was valid, and locked out the user if too many incorrect attempts. I’ll blog that if anyone is interested.

Even if you don’t want to do that, returning a single field is better achieved by using ExecuteScalar() and forgetting a data reader.

Microsoft have a developer how-to on injection.

[)amien

Visual Studio 2003 – System.ArgumentException in debugger

I recently ran into a problem while debugging inside Visual Studio 2003.Net. Google couldn’t find me an answer, only a few other people with the same problem. Here’s my solution in the hope it might save somebody else some time.

Symptoms

Whenever debugging a specific VB.NET application that used a C# class library I would receive the following error certain objects in the C# class library:

<error: an exception of type: {System.ArgumentException} occurred>

Strangely only the ASP.NET application was affected, the WinForms application that also used this class library was unaffected. It also seemed to be localized to the debugger only, run-time behavior appeared to be just fine.

A reboot, rebuild project or a clear down of the ASP temp directory had no effect and another developer on the project had exactly the same problem despite the fact we do not share any binaries…

Solution

Close VS and delete all bin and obj directories from all projects in your solution if you want to be totally sure.

If you really don’t want to do this, deleting the .pdb’s from the project where the messed up object lives may be enough.

You may think forcing a rebuild with “rebuild solution” would achieve this, but it does not.

Cause (optional)

While debugging I noticed that I couldn’t create the messed up object interactively either. It was complaining that no constructor took 4 arguments and that it took 3. Well, it used to take 3 – over a week ago.

This leads me to believe that some combination of changing your C# source can make either VS or the C# compiler believe the debugging symbols are still up to date when they are not. This may possibly only affect VB.NET applications compiled against it.

It must be a reproducible bug because it happened on two different machines.

[)amien