Scott Hanselman has blogged recently about our experience with nasty tricks Visual Studio debugger can play on you. Turns out, it's possible to run and debug normally without resorting to a secondary thread spin-off. But there is a price to pay: remote debugging for web services must be disabled. Add this to your app.config:
<configuration>
<system.diagnostics>
<switches>
<add name="Remote.Disable" value="1" />
</switches>
</system.diagnostics>
</configuration>
Sweet.
I already mused about NT Backup before. Since then things started to go a bit sideways in terms of reliability of disk backups immediately followed by Exchange 2003 backup. For some reason media manager refused to recognise that disk backup has finished and Exchange dump was consistently failing. Poking around yielded the following update to the batch (set names are now really nice :-).
@echo off
Rem get date into a vairable so we can create good media name
rem for /F "usebackq delims=" %%i IN (`date /t`) DO set dt="%%ibackup media"
for /f "Tokens=1-4 Delims=/ " %%i in ('date /t') do set dt=%%i-%%j-%%k-%%l
for /f "Tokens=1" %%i in ('time /t') do set tm=-%%i
set tm=%tm::=-%
set dtt=%dt%%tm%
@echo ---------------------------------------- > report.txt
@echo Backup job started %dtt% >> report.txt
@echo ---------------------------------------- >> report.txt
Rem Set folder name containing backup log files
set fld=C:\Documents and Settings\Administrator\Local Settings\Application Data\Microsoft\Windows NT\NTBackup\data\
@echo net stop "Removable Storage" >> report.txt
net stop "Removable Storage" >> report.txt
@echo ---------------------------------------- >> report.txt
rem refresh media (is that why backup is failing?!)
@echo rsm.exe refresh /LF"HP C1537A SCSI Sequential Device" >> report.txt
start /wait rsm.exe refresh /LF"HP C1537A SCSI Sequential Device" >> report.txt
@echo ---------------------------------------- >> report.txt
sleep 30
Rem Perform Files backup
@echo C:\WINNT\system32\ntbackup.exe backup "@C:\admin\files.bks" /N "%dtt%" /D "File daily %dtt%" /v:yes /r:no /rs:no /hc:on /m normal /J "File daily %dtt%" /l:s /P "4mm DDS" /UM >> report.txt
@echo ---------------------------------------- >> report.txt
C:\WINNT\system32\ntbackup.exe backup "@C:\admin\files.bks" /N "%dtt%" /D "File daily %dtt%" /v:yes /r:no /rs:no /hc:on /m normal /J "File daily %dtt%" /l:s /P "4mm DDS" /UM
Rem This dir command will list all log files sorted by date in bare format
set fcmd=dir "%fld%*.log" /OD /A-D /B
Rem set command will set lastf variable to the last file in set, i.e. the latest one
for /F "tokens=* delims= " %%i in ('%fcmd%') do set lastf=%fld%%%i
type "%lastf%" >> report.txt
sleep 120
rem refresh media (is that why backup is failing?!)
@echo rsm.exe refresh /LF"HP C1537A SCSI Sequential Device" >> report.txt
start /wait rsm.exe refresh /LF"HP C1537A SCSI Sequential Device" >> report.txt
@echo ---------------------------------------- >> report.txt
sleep 120
Rem Perform Exchange backup
@echo ---------------------------------------- >> report.txt
@echo C:\WINNT\system32\ntbackup.exe backup "@C:\admin\exchange.bks" /A /T %dtt% /D "Exchange Daily %dtt%" /V:yes /R:yes /RS:no /HC:on /M normal /J "Exchange Daily %dtt%" /L:s >> report.txt
@echo ---------------------------------------- >> report.txt
C:\WINNT\system32\ntbackup.exe backup "@C:\admin\exchange.bks" /A /T %dtt% /D "Exchange Daily %dtt%" /V:yes /R:yes /RS:no /HC:on /M normal /J "Exchange Daily %dtt%" /L:s
Rem This dir command will list all log files sorted by date in bare format
set fcmd=dir "%fld%*.log" /OD /A-D /B
Rem set command will set lastf variable to the last file in set, i.e. the latest one
for /F "tokens=* delims= " %%i in ('%fcmd%') do set lastf=%fld%%%i
type "%lastf%" >> report.txt
sleep 120
rem eject media
@echo ---------------------------------------- >> report.txt
@echo rsm.exe eject /PF"%dtt% - 1" /astart >> report.txt
@echo ---------------------------------------- >> report.txt
start /wait rsm.exe eject /PF"%dtt% - 1" /astart >> report.txt
sleep 30
c:\admin\blat report.txt -t youraddress@yourdomain.com -s "Backup Report" -mime -server yoursmtpserver -f "backup@yourdomain.com" -q
Rem Cleanup
set fld=
set fcmd=
set lastf=
set dt=
set tm=
set dtt=
Nothing makes our life more interesting than ASP.NET behaviour at debugger termination. This is a short version of three lost hours:
Create an empty page and add the following code
private void Page_Load(object sender, System.EventArgs e)
{
const string CONNECT_STRING = "server=(local);uid=sa;pwd=password;database=msdb;persist security info=True";
SqlConnection con = new SqlConnection(CONNECT_STRING);
con.Open();
SqlTransaction tran = con.BeginTransaction();
try
{
new SqlCommand("create table temptable(id int)", con, tran).ExecuteNonQuery();
tran.Commit();
}
catch
{
tran.Rollback();
throw;
}
finally
{
con.Close();
}
}
Add breakpoint at “new SqlCommand“ line and hit F5
When debugger stopped at breakpoint hit Shift-F5.
And here is trace from SQL Profiler:
SET TRANSACTION ISOLATION LEVEL READ COMMITTED;BEGIN TRANSACTION
go
create table temptable(id int)
go
COMMIT TRANSACTION
go
Seems that ASP.NET is entitled to do whatever it wants at debugger termination including successful execution of the entire program.
NOT sweet.
I was looking at Programming ASP.NET, Second Edition on Amazon and suddenly the question came up:
Why do we always expect software vendors to offer upgrades for their products but no one expects that for books? Is there some economical rationale I'm missing? How cool would it be to walk into a bookstore holding the first edition and to hear "Oh, yes, sir, this mighty book you've got in your hands qualifies you for an upgrade discount of 40%".
There is a handy class UriBuilder which, unlike Uri, allows full read-write access to all the components of Uri. However, problems started when I tried to serialise it using XmlSerializer. The following code:
using System;
using System.Collections;
using System.Xml;
using System.Xml.Serialization;
public class XmlPleaseIgnore
{
public static void Main()
{
UriBuilder b = new UriBuilder("http", "localhost", 80, "/test/default.aspx");
XmlSerializer x = new XmlSerializer(b.GetType());
x.Serialize(Console.Out, b);
}
}
quickly produced this:
Unhandled Exception: System.InvalidOperationException: There was an error reflecting type 'System.UriBuilder'. ---> System.InvalidOperationException: System.Uri cannot be serialized because it does not have a default public constructor.
A bit of digging revealed that UriBuilder.Serialize method
...converts the public fields and read/write properties of an object into XML. It does not convert methods, indexers, private fields, or read-only properties. To serialize all of an object's fields and properties, both public and private, use the BinaryFormatter.
And, of course, Uri property of UriBuilder is read-only property and, to make the matter worse, it does not have public default constructor. If it were my class I would apply XmlIgnore attribute to the member and that would do the trick. As I found, with a bit of code it can be done for external classes as well:
using System;
using System.Collections;
using System.Xml;
using System.Xml.Serialization;
public class XmlPleaseIgnore
{
public static void Main()
{
// add XmlIgnore attribute and attach it to Uri member of UriBuilder class
XmlAttributes attrs = new XmlAttributes();
attrs.XmlIgnore = true;
XmlAttributeOverrides over = new XmlAttributeOverrides();
over.Add( typeof(UriBuilder), "Uri", attrs);
UriBuilder b = new UriBuilder("http", "localhost", 80, "/test/default.aspx");
XmlSerializer x = new XmlSerializer(b.GetType(), over); // override attributes
x.Serialize(Console.Out, b);
}
}And here is the output:
xml version="1.0" encoding="utf-8" ?>
<UriBuilder
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<Fragment />
<Host>localhost< FONT>Host>
<Password />
<Path>/test/default.aspx< FONT>Path>
<Port>80< FONT>Port>
<Query />
<Scheme>http< FONT>Scheme>
<UserName />
< FONT>>
Note that attribute is applied to a member of a class and therefore I can also serialise my own classes which have UriBuilder as a member variable.
Sweet.
I did not realise that declared variables in SQL are effectively “static” when used in rowsets. That is, the following code:
create table roles(role nvarchar(50))
GO
insert roles
select 'Administrator' union
select 'Editor' union
select 'User'
go
declare @roles nvarchar(1024)
select @roles = isnull(@roles + ', ', '') + role from roles
select @roles
go
drop table roles
go
yeilds “Administrator, Editor, User“.
Wow! Now instead of returning rowset of roles for a user and then concatenating results in client-side code, I can just write a function similar to:
create function fnGetUserRoleVerbose(@userid int, @delim nvarchar(2) = ', ')
returns nvarchar(1024)
as
begin
declare @role nvarchar(1024)
select
@role = isnull(@role + @delim, '') + roles.name
from
users
inner join users_roles
on users.id = userid
inner join roles
on roleid = roles.id
where
users.id = @userid
return @role
end
and then just do this:
string roles = (string) SqlHelper.ExecuteScalar(
myConnectionString,
CommandType.Text,
string.Format("select dbo.fnGetUserRoleVerbose({0}, default)", userId));
Could not google this morning... Dig deeper...
C:\>nslookup www.google.com ns1.google.com
Server: ns1.google.com
Address: 216.239.32.10
*** ns1.google.com can't find www.google.com: Non-existent domain
I... can... work... without... Google... Must... continue... Try local brew...
C:\>nslookup www.google.com.au ns1.google.com
Server: ns1.google.com
Address: 216.239.32.10
Non-authoritative answer:
Name: www.google.akadns.net
Address: 216.239.53.99
Aliases: www.google.com.au
Phew! Must be Anti-Oz conspiracy!
Was driving to the customer site in a really bad mood but one of the billboards made me laugh all the way: MBF - Australian health fund/private health insurer has a new motto: "BE POSITIVE". Way to go, PR boneheads! That's exactly what I don't want to be when it comes to my health :-)
Did you know that in date fields in Outlook (Task, Appointment forms) you can type things like "tomorrow", "yesterday", "week from now", "in 2 days", "two weeks ago", "next year". Even "Christmas", "Boxing Day", "New Years Day", "Halloween", "Cinco de Mayo" and "Independence day" work (though "Queens Birthday" and "tonight" do not :-)
Very handy when your client says "lets talk again in three days" - saves you figuring out what date it's going to be in case you've been on a coding roll for 20 hours straight and lost track of time. Sweet.
I'm yet to see
ASP.NET code which does not use '~' mapping feature in Server.MapPath. We all
love this feature, except... wait a minute... it is not documented?!
Search on MSDN failed to uncover any statement stating that '~' in MapPath
function refers to a virtual root (the task was not made any easier by the fact
that tilde is ignored in a search). Could it be that we're all typing megalines
of code against undocumented feature? And what, apart from bazillions of angered
developers around the world, will stop ASP.NET team from changing it
in 2.0 from '~' to, say '$', 'œ', '¥' or '½', for this matter
:-).
Now, here is the
georged™
challenge: the first person who points me to a piece of documentation explaining
usage of a tilde character in MapPath is entitled to AUS $25 (about US$17 and rising).
Money will be transferred to a PayPal
account or similar.
Rules:
- It must be
official documentation, i.e. references to samples, user groups,
magazine articles, blogs, etc will not be considered.
- 'First' will be
judged by UTC time of the reply/post/email.
- I decide who's
first and whether pointer/answer is correct.
- if(you.disagree)
goto 3;
- Ah, what the heck,
Microsoft employees are also eligible to enter :-)
Anyone?
Cheers
Georged
More Posts
Next page »