Monday, September 29, 2008

Do-not-break-the-testcases 101

Before commiting code to source control:

1. Ensure that you have the latest of every one else's changes.
2. Build and compile everything.
3. Run ALL testcases.
4. Should any testcase fail - take care of it BEFORE commiting.
5. Make sure to check in ALL files necessary, including .csproj, data files, and testcases. Do a "show all differences" to make sure you're not missing any changed or added files.
6. Do not be afraid to ask someone to retrieve your changes to verify that they are ok.

I wrote these rules on a recent project I was on. These are the basic steps, there are more that have been said better by others before me, such as "Commit early, Commit often", and "Do not commit and run", and tools such as Cruise control to make your life easier.

Friday, September 5, 2008

Chrome

So, Google builds a web browser that is better able to handle javascript applications - such as Google Apps. The cynical part of me would say that Google is trying to solve a problem they themselves created. 

I use GMail, Google Reader and Google Docs regularly and I'm absolutely delighted at the improved performance and stability. 

Ofcourse, running Google's apps in Google's browser has the smell of monopoly attached to it. But I'm too high on all the fresh air to care.

All hail our new evil overlords!

Wednesday, May 7, 2008

Nullable enums

Enums in .net are represented as integers, right? This means you can do the following (yes, casting the integer to object is superfluous. It is added for clarity):
public enum Foo { X, Y };
[...]
public Foo Bar { set { ... } }
[...]
PropertyInfo prop = obj.GetType().GetProperty("Bar");
prop.SetValue(obj, (object)1, null);

I.e. you can use reflection to set the value of a property of an enum type using its integer value. (You could also just cast it like so: obj.Bar = (Foo)1;, but that's not quite as interesting, for reasons that will be revealed later.)

Also, a boxed value and a boxed nullable value have the same representation, right? They are both pointers to a small object containing the primitive. As the following code shows, boxed integers and boxed nullable integers are interchangeable:

int intValue = 1;
int? nullableIntValue = 1;
object boxedInt = intValue;
object boxedNullableInt = nullableIntValue;
intValue = (int)boxedNullableInt;
nullableIntValue = (int?)boxedInt;

And, not surprisingly, the same holds for enum values, now demonstrated using reflection as in our first example:

public Foo? Bar { set { ... } }
[...]
prop.SetValue(obj, (object)Foo.X, null);

Now, combining these two examples, what do you think the following code would do?

public Foo? Bar { set { ... } }
[...]
prop.SetValue(obj, (object)1, null);

If you, like me, thought it would happily set the property to the enum value correspoding to 1, in this case Foo.X, you'd be wrong. Instead it throws the following exception:

Object of type 'System.Int32' cannot be converted to type 'System.Nullable`1

Huh? The .net designers may know why this is, and they may even have a good reason for it, but to me it just seems plain weird. It is worth noting that obj.Bar = (Foo?)1; works just fine.

This issue came up when mapping objects from database tables, using integers to represent enum values. I've reluctantly added the following workaround to my mapping code:

PropertyInfo prop = [...]
object val = [...]
if ((prop.PropertyType.IsGenericType) &&
(prop.PropertyType.GetGenericTypeDefinition() == typeof(Nullable<>)))
{
Type targetType = prop.PropertyType.GetGenericArguments()[0];
if (targetType.IsEnum)
val = Enum.ToObject(targetType, val);
}
prop.SetValue(obj, val, null);

Sunday, April 20, 2008

Bad service

It appears there is a problem with Sql Server Service Broker when executing DROP SERVICE. I had it choke the server to eventually fail with the message "There is insufficient system memory to run this query".

The problem is that Service Broker needs to end all open conversations, and it does so in one transaction. If you have many open conversations troubles arise.

I found the following link which describes a fix for the problem. I'm reprinting the fix here in case the link goes bad:

declare crsConversations cursor for
select conversation_handle
from sys.conversation_endpoints e
join sys.services s on s.service_id = e.service_id
where s.name = 'myservicename';
open crsConversations;
declare @dh uniqueidentifier;
declare @batch int;
select @batch = 0;
begin transaction
fetch next from crsConversations into @dh;
while @@fetch_status =0
begin
-- replace this with end conversation @dh with error ... if
-- and error has to be sent to the peer
--
end conversation @dh with cleanup;
-- commit every 1000 conversations ended
select @batch = @batch + 1;
if @batch > 999
begin
commit transaction;
begin transaction;
end
fetch next from crsConversations into @dh;
end
commit transaction;
close crsConversations;
deallocate crsConversations;
go
drop service 'myservicename'
go


Now, if you have this problem the real question is, why are you leaking open conversations?

Saturday, April 19, 2008

(contd.)

My old blog can be found here:
http://blogs.codegear.com/andersivner.
I've since then left Borland/CodeGear and now work for Sigma.

The product I worked on at Borland/CodeGear, ECO, is now available from CapableObjects, a company run by my friends and former collegues. They have recently released ECO for Visual Studio. Way cool!